// SMSC.TJ API для C++ Builder (smsc.tj) версия 2.1 (03.07.2019) #include #include #include #include #include #include # include // Константы с параметрами отправки const char* SMSC_LOGIN = "login"; // логин клиента const char* SMSC_PASSWORD = "password"; // пароль или MD5-хеш пароля в нижнем регистре Boolean SMSC_POST = false; // использовать метод POST const char* SMSC_CHARSET = #ifdef _DELPHI_STRING_UNICODE "utf-8"; #else "windows-1251"; #endif // кодировка сообщения (utf-8 или koi8-r), по умолчанию используется windows-1251 const Boolean SMSC_DEBUG = false; // флаг отладки // Константы для отправки SMS по SMTP const char* SMTP_FROM = "api@smsc.tj"; // e-mail адрес отправителя const char* SMTP_SERVER = "send.smsc.tj"; // адрес smtp сервера const char* SMTP_LOGIN = ""; // логин для smtp сервера const char* SMTP_PASSWORD = ""; // пароль для smtp сервера typedef DynamicArray < DynamicArray < String > > T2DStringDynArray; class SMSC { public: // Динамический двумерный строковый массив T2DStringDynArray D2Res; // Общедоступные методы класса SMSС // // Метод отправки SMS // // обязательные параметры: // // phones - список телефонов через запятую или точку с запятой // mes - отправляемое сообщение // // необязательные параметры: // // translit - переводить или нет в транслит // time - необходимое время доставки в виде строки (DDMMYYhhmm, h1-h2, 0ts, +m) // id - идентификатор сообщения. Представляет собой 32-битное число в диапазоне от 1 до 2147483647. // format - формат сообщения (0 - обычное sms, 1 - flash-sms, 2 - wap-push, 3 - hlr, 4 - bin, 5 - bin-hex, 6 - ping-sms, 7 - mms, 8 - mail, 9 - call, 10 - viber, 11 - soc) // sender - имя отправителя (Sender ID). // query - строка дополнительных параметров, добавляемая в URL-запрос ("valid=01:00&maxsms=3") // files - массив путей к файлам при отправке mms или e-mail сообщений // // возвращает массив (, <количество sms>, <стоимость>, <баланс>) в случае успешной отправки // либо (, -<код ошибки>) в случае ошибки TStringDynArray send_sms(String phones, String mes, int translit = 0, String time = "", int id = 0, int format = 0, String sender = "", String query = "", TStringList *files = NULL) { TStringDynArray m; if (files != NULL) SMSC_POST = true; String formats[11] = {"flash=1", "push=1", "hlr=1", "bin=1", "bin=2", "ping=1", "mms=1", "mail=1", "call=1", "viber=1", "soc=1"}; m = _smsc_send_cmd("send", "cost=3&phones=" + _urlencode(phones) + "&mes=" + _urlencode(mes) + "&id=" + IntToStr(id) + "&translit=" + IntToStr(translit) + (format > 0 ? "&" + formats[format-1] : String("")) + (sender != "" ? "&sender=" + _urlencode(sender) : String("")) + (time != "" ? "&time=" + _urlencode(time) : String("")) + (query != "" ? "&" + query : String("")), files); // (id, cnt, cost, balance) или (id, -error) if (SMSC_DEBUG) if (StrToInt(m[1]) > 0) _print_debug("Сообщение отправлено успешно. ID: " + m[0] + ", всего SMS: " + m[1] + ", стоимость: " + m[2] + ", баланс: " + m[3]); else _print_debug("Ошибка №" + m[1].SubString(2, 1) + ", ID: " + m[0]); return m; } // SMTP версия метода отправки SMS void send_sms_mail(String phones, String mes, int translit = 0, String time = "", int id = 0, int format = 0, String senderid = "") { TIdMessage* msg = new TIdMessage(0); TIdSMTP* idsmtp = new TIdSMTP(0); idsmtp->Port = 25; idsmtp->Host = SMTP_SERVER; if (SMTP_LOGIN != "") { idsmtp->Username = SMTP_LOGIN; idsmtp->Password = SMTP_PASSWORD; } msg->Recipients->EMailAddresses = "send@send.smsc.tj"; msg->From->Address = SMTP_FROM; msg->ContentType = "text/plain"; msg->CharSet = SMSC_CHARSET; SysLocale.PriLangID = LANG_SYSTEM_DEFAULT; // если не поставить, то Indy будет оборачивать в koi8-r заголовки msg->Body->Add(SMSC_LOGIN + String(':') + SMSC_PASSWORD + String(':') + IntToStr(id) + String(':') + time + String(':') + IntToStr(translit) + String(',') + IntToStr(format) + String(',') + senderid + String(':') + phones + String(':') + mes); idsmtp->Connect(); idsmtp->Send(msg); idsmtp->Disconnect(); idsmtp->Free(); msg->Free(); } // Метод получения стоимости SMS // // обязательные параметры: // // phones - список телефонов через запятую или точку с запятой // message - отправляемое сообщение // // необязательные параметры: // // translit - переводить или нет в транслит // format - формат сообщения (0 - обычное sms, 1 - flash-sms, 2 - wap-push, 3 - hlr, 4 - bin, 5 - bin-hex, 6 - ping-sms, 7 - mms, 8 - mail, 9 - call, 10 - viber, 11 - soc) // sender - имя отправителя (Sender ID) // query - строка дополнительных параметров, добавляемая в URL-запрос ("list=79999999999:Ваш пароль: 123\n78888888888:Ваш пароль: 456") // // возвращает массив (<стоимость>, <количество sms>) либо (0, -<код ошибки>) в случае ошибки TStringDynArray get_sms_cost(String phones, String mes, int translit = 0, int format = 0, String sender = "", String query = "") { TStringDynArray m; String formats[11] = {"flash=1", "push=1", "hlr=1", "bin=1", "bin=2", "ping=1", "mms=1", "mail=1", "call=1", "viber=1", "soc=1"}; m = _smsc_send_cmd("send", "cost=1&phones=" + _urlencode(phones) + "&mes=" + _urlencode(mes) + "&translit=" + IntToStr(translit) + (format > 0 ? "&" + formats[format-1] : String("")) + (sender != "" ? "&sender=" + _urlencode(sender) : String("")) + (query != "" ? "&" + query : String(""))); // (cost, cnt) или (0, -error) if (SMSC_DEBUG) if (StrToInt(m[1]) > 0) _print_debug("Стоимость рассылки: " + m[0] + ". Всего SMS: " + m[1]); else _print_debug("Ошибка №" + m[1].SubString(2, 1)); return m; } // Метод проверки статуса отправленного SMS или HLR-запроса // // id - ID cообщения или список ID через запятую // phone - номер телефона или список номеров через запятую // all - вернуть все данные отправленного SMS, включая текст сообщения (0,1 или 2) // // возвращает массив (для множественного запроса возвращается массив с единственным элементом, равным 1. В этом случае статусы сохраняются в // двумерном динамическом массиве класса D2Res): // // для одиночного SMS-сообщения: // (<статус>, <время изменения>, <код ошибки доставки>) // // для HLR-запроса: // (<статус>, <время изменения>, <код ошибки sms>, <код IMSI SIM-карты>, <номер сервис-центра>, <код страны регистрации>, <код оператора>, // <название страны регистрации>, <название оператора>, <название роуминговой страны>, <название роумингового оператора>) // // при all = 1 дополнительно возвращаются элементы в конце массива: // (<время отправки>, <номер телефона>, <стоимость>, , <название статуса>, <текст сообщения>) // // при all = 2 дополнительно возвращаются элементы <страна>, <оператор> и <регион> // // при множественном запросе (данные по статусам сохраняются в двумерном массиве D2Res): // если all = 0, то для каждого сообщения или HLR-запроса дополнительно возвращается и <номер телефона> // // если all = 1 или all = 2, то в ответ добавляется // // либо массив (0, -<код ошибки>) в случае ошибки TStringDynArray get_status(String id, String phone, int all = 0) { TIME_ZONE_INFORMATION TZInfo; TStringDynArray m; String ans; int i; m = _smsc_send_cmd("status", "phone=" + _urlencode(phone) + "&id=" + _urlencode(id) + "&all=" + IntToStr(all)); // (status, time, err, ...) или (0, -error) if (id.Pos(",") == 0) { if (SMSC_DEBUG) if ((m[1] != "") && (StrToInt(m[1]) >= 0)) { String ans = "Статус SMS = " + m[0]; GetTimeZoneInformation(&TZInfo); if (StrToInt(m[1]) > 0) ans = ans + ", время изменения статуса - " + DateTimeToStr(UnixToDateTime(StrToInt64(m[1]) - TZInfo.Bias * 60)); _print_debug(ans); } else _print_debug("Ошибка №" + m[1].SubString(2, 1)); int idx = all == 1 ? 9 : 12; if (all > 0 && m.Length > idx && (m.Length < idx + 5 || m[idx + 5] != "HLR")) { ans = ""; for (i = 0; i < m.Length; i++) ans += m[i] + (i == m.Length - 1 ? "" : ","); m.Length = idx; for (i = 0; i < idx - 1; i++) SplitString(ans, ",", m[i], ans); m[idx - 1] = ans; } } else { if (m.Length == 1 && m[0].Pos("-") == 3) return SplitString(m[0], ","); D2Res.Length = 0; D2Res.Length = m.Length; for (i = 0; i < D2Res.Length; i++) D2Res[i] = SplitString(m[i], ","); m.Length = 1; m[0] = "1"; } return m; } // Метод получения баланса // // без параметров // // возвращает баланс в виде строки или пустую строку в случае ошибки String get_balance(void) { TStringDynArray m; m = _smsc_send_cmd("balance", ""); // (balance) или (0, -error) if (SMSC_DEBUG) if (m.Length == 1) _print_debug("Сумма на счете: " + m[0]); else _print_debug("Ошибка №" + m[1].SubString(2, 1)); return m.Length == 1 ? m[0] : String(""); } private: // Приватные методы класса SMSС // // Метод вызова запроса. Формирует URL и делает 3 попытки чтения TStringDynArray _smsc_send_cmd(String cmd, String arg, TStringList *files = NULL) { TIdHTTP* idhttp = new TIdHTTP(0); String s, delim, pl, pr, url, _url; int i, cnt = 0; arg = "login=" + _urlencode(SMSC_LOGIN) + "&psw=" + _urlencode(SMSC_PASSWORD) + "&fmt=1&charset=" + String(SMSC_CHARSET) + "&" + arg; url = _url = "http://smsc.tj/sys/" + cmd + ".php"; do { if (cnt++) url = StringReplace(_url, "smsc.tj", "www" + IntToStr(cnt) + ".smsc.tj", TReplaceFlags() << rfReplaceAll); try { if (SMSC_POST) { TIdMultiPartFormDataStream *par = new TIdMultiPartFormDataStream(); for (i = 0; i < files->Count; i++) par->AddFile("File" + IntToStr(i), files->Strings[i], "application/octet-stream"); TStringDynArray params = SplitString(arg, "&"); String VLeft, VRight; TIdFormDataField *pf; for (i = 0; i < params.Length; i++) { SplitString(params[i], "=", VLeft, VRight); pf = par->AddFormField(VLeft, VRight); pf->Charset = SMSC_CHARSET; pf->ContentTransfer = "binary"; } s = idhttp->Post(url, par); delete par; par = NULL; } else s = idhttp->Get(url + "?" + arg); } catch (...) { s = ""; } } while ((s == "") && (cnt < 5)); if (s == "") { if (SMSC_DEBUG) _print_debug("Ошибка чтения адреса: " + url + "?" + arg); s = ","; // фиктивный ответ } delete idhttp; idhttp = NULL; delim = ","; if (cmd == "status") { TStringDynArray par = SplitString(arg, "&"); for (i = 0; i < par.Length; i++) { SplitString(par[i], "=", pl, pr); if (pl == "id" && pr.Pos("%2C") > 1) // запятая в id - множественный запрос delim = "\n"; } } return SplitString(s, delim); } // кодирование параметра в http-запросе String _urlencode(String str) { if (SMSC_POST) return str; String UnsafeChars = "!\"#%&'*,:;<=>?[]^`{|} "; String EncodeStr = ""; UTF8String UStr; int i, j; for (i = 1; i <= str.Length(); i++) if (CharIsInSet(str, i, UnsafeChars) || !CharIsInSet(str, i, CharRange(Char(33), Char(126)))) { UStr = str[i]; for (j = 1; j <= UStr.Length(); j++) EncodeStr += "%" + IntToHex(Byte(UStr[j]), 2); } else EncodeStr += str[i]; return EncodeStr; } // вывод отладочной информации void _print_debug(String str) { ShowMessage(str); } }; // Examples: // TStringDynArray ret; // SMSC* sms = new SMSC; // String balance; // // ret = sms->send_sms("79999999999", "Ваш пароль: 123", 1); // ret = sms->send_sms("79999999999", "http://smsc.tj\nSMSC.TJ", 0, "", 0, 0, "", "maxsms=3"); // ret = sms->send_sms("79999999999", "0605040B8423F0DC0601AE02056A0045C60C036D79736974652E72750001036D7973697465000101", 0, "", 0, 5); // ret = sms->send_sms("79999999999", "", 0, "", 0, 3); // ret = sms->send_sms("dest@mysite.com", "Ваш пароль: 123", 0, 0, 0, 8, "source@mysite.com", "subj=Confirmation"); // ret = sms->get_sms_cost("79999999999", "Вы успешно зарегистрированы!"); // sms->send_sms_mail("79999999999", "Ваш пароль: 123", 0, "0101121000"); // ret = sms->get_status(12345, "79999999999"); // balance = sms->get_balance(); // // delete sms;