├── README.md └── amxmodx └── scripting ├── Example.sma ├── TelegramBotAPI.sma └── include └── TelegramBotAPI.inc /README.md: -------------------------------------------------------------------------------- 1 | # [Telegram BOT API](https://core.telegram.org/api) wrapper library for [AMXModX](github.com/alliedmodders/amxmodx) plugins. 2 | 3 | Requirments: 4 | - [GoldSrc RestInPawn module](https://github.com/In-line/grip) 5 | 6 | Include file API: [TelegramBotAPI.inc](https://github.com/wopox1337/AMXX_TelegramBotAPI/blob/master/amxmodx/scripting/include/TelegramBotAPI.inc) 7 | 8 | * Create telegram bot: [LINK](https://core.telegram.org/bots#6-botfather) 9 | * Get bot_token: [LINK](https://www.google.com/search?q=how+to+get+bot+token+telegram&oq=How+to+get+bot+token) 10 | * Get chat_id: [LINK](https://www.google.com/search?q=how+to+get+telegram+%22chat_id) 11 | 12 | Usage example: 13 | ```C 14 | #include 15 | #include 16 | 17 | new const BOT_TOKEN[] = " ENTER YOU KEY HERE "; 18 | 19 | // Chats ID configuration. 20 | new const CHATS[][] = { 21 | "207444577" 22 | ,"-1001431316463" 23 | }; 24 | 25 | new g_botID; 26 | 27 | public plugin_init() { 28 | register_plugin("[Example plugin] Send server startup to TG", "1.0.0", "Sergey Shorokhov"); 29 | } 30 | 31 | public TG_BotAPI_Initialized() { 32 | TG_RegisterBot(BOT_TOKEN, "BotRegistred"); 33 | } 34 | 35 | public BotRegistred(const botID, const botName[]) { 36 | g_botID = botID; 37 | for(new i; i < sizeof CHATS; i++) { 38 | TG_BotAddSubscription(botID, CHATS[i], "BotSubscriptionAdded"); 39 | } 40 | } 41 | 42 | public BotSubscriptionAdded(const subscriptionID, const botID, const chatID[], const chatTitle[], const chatType[]) { 43 | 44 | } 45 | 46 | public OnConfigsExecuted() { 47 | if(!TG_BotAPI_Allowed()) 48 | return; 49 | 50 | TG_BotSendMessageALL(g_botID, "[Server startup] - TG_BotSendMessageALL"); 51 | //TG_BotSendMessage(g_botID, "-1001431316463", "[Server startup] - TG_BotSendMessage"); 52 | } 53 | ``` 54 | -------------------------------------------------------------------------------- /amxmodx/scripting/Example.sma: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #pragma semicolon 1 5 | #pragma ctrlchar '\' 6 | 7 | new const BOT_TOKEN[] = " ENTER YOU KEY HERE "; 8 | 9 | // Chats ID configuration. 10 | new const CHATS[][] = { 11 | "207444577" 12 | ,"-1001431316463" 13 | }; 14 | 15 | new g_botID; 16 | 17 | public plugin_init() { 18 | register_plugin("[Example plugin] Send server startup to TG", "1.0.0", "Sergey Shorokhov"); 19 | } 20 | 21 | public TG_BotAPI_Initialized() { 22 | TG_RegisterBot(BOT_TOKEN, "BotRegistred"); 23 | } 24 | 25 | public BotRegistred(const botID, const botName[]) { 26 | g_botID = botID; 27 | for(new i; i < sizeof CHATS; i++) { 28 | TG_BotAddSubscription(botID, CHATS[i], "BotSubscriptionAdded"); 29 | } 30 | } 31 | 32 | public BotSubscriptionAdded(const subscriptionID, const botID, const chatID[], const chatTitle[], const chatType[]) { 33 | 34 | } 35 | 36 | public OnConfigsExecuted() { 37 | if(!TG_BotAPI_Allowed()) 38 | return; 39 | 40 | TG_BotSendMessageALL(g_botID, "[Server startup] - TG_BotSendMessageALL"); 41 | //TG_BotSendMessage(g_botID, "-1001431316463", "[Server startup] - TG_BotSendMessage"); 42 | } -------------------------------------------------------------------------------- /amxmodx/scripting/TelegramBotAPI.sma: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #pragma semicolon 1 7 | #pragma ctrlchar '\' 8 | #pragma dynamic 524288 9 | 10 | 11 | #define print(%1) server_print(%1) 12 | 13 | new const URL_API[] = "https://api.telegram.org/"; 14 | new GripRequestOptions: g_pReqOptions; 15 | 16 | 17 | new g_fwResult; 18 | new g_fwAPI_Initialized; 19 | 20 | 21 | enum APIState_s { 22 | m_Init = 123, 23 | m_RegisterBot, 24 | m_BotSubscriptions, 25 | m_SendMessage, 26 | m_Ready 27 | } 28 | 29 | enum RequestData_s { 30 | APIState_s: rd_state, 31 | rd_callBack[32], 32 | rd_token[64] 33 | } 34 | 35 | new bool: g_bInitialized; 36 | 37 | enum BotData_s { 38 | bot_Name[64], 39 | bot_Token[64], 40 | Array:bot_Subscriptions 41 | } 42 | new Array:g_aBotsArray; 43 | 44 | public plugin_precache() { 45 | register_plugin("Telegram Bot API", "1.0.0", "SergeyShorokhov"); 46 | 47 | // Requests prepare 48 | g_pReqOptions = grip_create_default_options(.timeout = 30.0); 49 | grip_options_add_header(g_pReqOptions, "Content-Type", "application/json"); 50 | 51 | // Test connect, initialize API 52 | new DataPack: reqData = CreateDataPack(); 53 | WritePackCell(reqData, m_Init); 54 | ResetPack(reqData); 55 | 56 | new GripBody:body = grip_body_from_string(""); 57 | grip_request(URL_API, body, GripRequestTypeGet, "HandleRequest", g_pReqOptions, reqData); 58 | grip_destroy_body(body); 59 | } 60 | 61 | // *** Natives *** // 62 | public plugin_natives() { 63 | register_library("telegram_bot_api"); 64 | 65 | register_native("TG_RegisterBot", "native_RegisterBot"); 66 | register_native("TG_BotAddSubscription", "native_BotAddSubscription"); 67 | register_native("TG_BotSendMessageALL", "native_BotSendMessageALL"); 68 | register_native("TG_BotSendMessage", "native_BotSendMessage"); 69 | register_native("TG_BotAPI_Allowed", "native_BotAPI_Allowed"); 70 | } 71 | 72 | public native_RegisterBot(pluginID, argc) { 73 | enum { arg_token = 1, arg_callBack }; 74 | 75 | if(argc != 2) { 76 | log_error(AMX_ERR_NATIVE, "Bad arguments num, expected 2, passed %d", argc); 77 | return 0; 78 | } 79 | 80 | if(!g_bInitialized) { 81 | log_error(AMX_ERR_NATIVE, "API not initialized!"); 82 | return 0; 83 | } 84 | 85 | new botToken[64]; 86 | get_string(arg_token, botToken, charsmax(botToken)); 87 | 88 | new callBack[32]; 89 | get_string(arg_callBack, callBack, charsmax(callBack)); 90 | new funcID = get_func_id(callBack, pluginID); 91 | if(funcID == -1) { 92 | log_error(AMX_ERR_NATIVE, "Function `%s` not found!", callBack); 93 | return 0; 94 | } 95 | 96 | // Send req 97 | new DataPack: reqData = CreateDataPack(); 98 | WritePackCell(reqData, m_RegisterBot); 99 | WritePackString(reqData, botToken); 100 | WritePackCell(reqData, funcID); 101 | WritePackCell(reqData, pluginID); 102 | ResetPack(reqData); 103 | 104 | new url[256]; 105 | formatex(url, charsmax(url), "%sbot%s/getMe", URL_API, botToken); 106 | 107 | new GripBody:body = grip_body_from_string(""); 108 | grip_request(url, body, GripRequestTypeGet, "HandleRequest", g_pReqOptions, reqData); 109 | grip_destroy_body(body); 110 | 111 | return 1; 112 | } 113 | 114 | public native_BotAddSubscription(pluginID, argc) { 115 | enum { arg_botID = 1, arg_chatID, arg_callBack }; 116 | 117 | if(argc != 3) { 118 | log_error(AMX_ERR_NATIVE, "Bad arguments num, expected 2, passed %d", argc); 119 | return -1; 120 | } 121 | 122 | if(!g_bInitialized) { 123 | log_error(AMX_ERR_NATIVE, "API not initialized!"); 124 | return -1; 125 | } 126 | 127 | new registredBotsCount = ArraySize(g_aBotsArray); 128 | if(registredBotsCount < 1) { 129 | log_error(AMX_ERR_NATIVE, "Haven't registred bots."); 130 | return -1; 131 | } 132 | 133 | new botIndexEntry = get_param(arg_botID) - 1; 134 | if(botIndexEntry > registredBotsCount) { 135 | log_error(AMX_ERR_NATIVE, "TG Bot out of range (index:%i, max:%i)", botIndexEntry, registredBotsCount); 136 | return -1; 137 | } 138 | 139 | new chatID[64]; 140 | get_string(arg_chatID, chatID, charsmax(chatID)); 141 | 142 | new callBack[32]; 143 | get_string(arg_callBack, callBack, charsmax(callBack)); 144 | new funcID = get_func_id(callBack, pluginID); 145 | if(funcID == -1) { 146 | log_error(AMX_ERR_NATIVE, "Function `%s` not found!", callBack); 147 | return -1; 148 | } 149 | 150 | new DataPack: reqData = CreateDataPack(); 151 | WritePackCell(reqData, m_BotSubscriptions); 152 | 153 | WritePackCell(reqData, botIndexEntry); 154 | WritePackString(reqData, chatID); 155 | WritePackCell(reqData, funcID); 156 | WritePackCell(reqData, pluginID); 157 | ResetPack(reqData); 158 | 159 | new botData[BotData_s]; 160 | ArrayGetArray(g_aBotsArray, botIndexEntry, botData); 161 | new botToken[64]; 162 | copy(botToken, charsmax(botToken), botData[bot_Token]); 163 | 164 | new url[256]; 165 | formatex(url, charsmax(url), "%sbot%s/getChat", URL_API, botToken); 166 | 167 | new GripJSONValue: bodyJSON = grip_json_init_object(); 168 | grip_json_object_set_string(bodyJSON, "chat_id", chatID); 169 | 170 | new GripBody:body = grip_body_from_json(bodyJSON); 171 | grip_request(url, body, GripRequestTypePost, "HandleRequest", g_pReqOptions, reqData); 172 | grip_destroy_body(body); 173 | 174 | return 1; 175 | } 176 | 177 | public native_BotSendMessageALL(pluginID, argc) { 178 | enum { arg_botID = 1, arg_message }; 179 | 180 | if(argc != 2) { 181 | log_error(AMX_ERR_NATIVE, "Bad arguments num, expected 2, passed %d", argc); 182 | return 0; 183 | } 184 | 185 | if(!g_bInitialized) { 186 | log_error(AMX_ERR_NATIVE, "API not initialized!"); 187 | return 0; 188 | } 189 | 190 | new registredBotsCount = ArraySize(g_aBotsArray); 191 | if(registredBotsCount < 1) { 192 | log_error(AMX_ERR_NATIVE, "Haven't registred bots."); 193 | return 0; 194 | } 195 | 196 | new message[2048]; 197 | get_string(arg_message, message, charsmax(message)); 198 | if(!strlen(message)) { 199 | log_error(AMX_ERR_NATIVE, "Can't send empty message (message:'%s')", message); 200 | return 0; 201 | } 202 | 203 | new botIndexEntry = get_param(arg_botID); 204 | if(botIndexEntry > registredBotsCount) { 205 | log_error(AMX_ERR_NATIVE, "TG Bot out of range (index:%i, max:%i)", botIndexEntry, registredBotsCount); 206 | return 0; 207 | } 208 | 209 | new botData[BotData_s]; 210 | ArrayGetArray(g_aBotsArray, botIndexEntry, botData); 211 | 212 | new subscriptionsCount = ArraySize(botData[bot_Subscriptions]); 213 | if(!subscriptionsCount) { 214 | log_error(AMX_ERR_NATIVE, "TG Bot haven't any subscriptions."); 215 | return 0; 216 | } 217 | 218 | new url[256]; 219 | formatex(url, charsmax(url), "%sbot%s/sendMessage", URL_API, botData[bot_Token]); 220 | 221 | new GripBody:body; 222 | new GripJSONValue: bodyJSON = grip_json_init_object(); 223 | grip_json_object_set_string(bodyJSON, "text", message); 224 | 225 | for(new i; i < subscriptionsCount; i++) { 226 | new DataPack: reqData = CreateDataPack(); 227 | WritePackCell(reqData, m_SendMessage); 228 | ResetPack(reqData); 229 | 230 | new chat_id[64]; 231 | ArrayGetString(botData[bot_Subscriptions], i, chat_id, charsmax(chat_id)); 232 | grip_json_object_set_string(bodyJSON, "chat_id", chat_id); 233 | 234 | body = grip_body_from_json(bodyJSON); 235 | grip_request(url, body, GripRequestTypePost, "HandleRequest", g_pReqOptions, reqData); 236 | } 237 | grip_destroy_body(body); 238 | 239 | return 1; 240 | } 241 | 242 | public native_BotSendMessage(pluginID, argc) { 243 | enum { arg_botID = 1, arg_chatID, arg_message }; 244 | 245 | if(argc != 3) { 246 | log_error(AMX_ERR_NATIVE, "Bad arguments num, expected 3, passed %d", argc); 247 | return 0; 248 | } 249 | 250 | if(!g_bInitialized) { 251 | log_error(AMX_ERR_NATIVE, "API not initialized!"); 252 | return 0; 253 | } 254 | 255 | new registredBotsCount = ArraySize(g_aBotsArray); 256 | if(registredBotsCount < 1) { 257 | log_error(AMX_ERR_NATIVE, "Haven't registred bots."); 258 | return 0; 259 | } 260 | 261 | new message[2048]; 262 | get_string(arg_message, message, charsmax(message)); 263 | if(!strlen(message)) { 264 | log_error(AMX_ERR_NATIVE, "Can't send empty message (message:'%s')", message); 265 | return 0; 266 | } 267 | 268 | new botIndexEntry = get_param(arg_botID); 269 | if(botIndexEntry > registredBotsCount) { 270 | log_error(AMX_ERR_NATIVE, "TG Bot out of range (index:%i, max:%i)", botIndexEntry, registredBotsCount); 271 | return 0; 272 | } 273 | 274 | new botData[BotData_s]; 275 | ArrayGetArray(g_aBotsArray, botIndexEntry, botData); 276 | 277 | new url[256]; 278 | formatex(url, charsmax(url), "%sbot%s/sendMessage", URL_API, botData[bot_Token]); 279 | 280 | new GripBody:body; 281 | new GripJSONValue: bodyJSON = grip_json_init_object(); 282 | grip_json_object_set_string(bodyJSON, "text", message); 283 | 284 | new DataPack: reqData = CreateDataPack(); 285 | WritePackCell(reqData, m_SendMessage); 286 | ResetPack(reqData); 287 | 288 | new chat_id[64]; 289 | get_string(arg_chatID, chat_id, charsmax(chat_id)); 290 | grip_json_object_set_string(bodyJSON, "chat_id", chat_id); 291 | 292 | body = grip_body_from_json(bodyJSON); 293 | grip_request(url, body, GripRequestTypePost, "HandleRequest", g_pReqOptions, reqData); 294 | 295 | grip_destroy_body(body); 296 | 297 | return 1; 298 | } 299 | 300 | public native_BotAPI_Allowed(pluginID, argc) { 301 | return g_bInitialized; 302 | } 303 | /////////////////////////// 304 | 305 | public HandleRequest(DataPack: reqData) { 306 | new GripResponseState: response_state = grip_get_response_state(); 307 | switch(response_state) { 308 | case GripResponseStateError, GripResponseStateTimeout, GripResponseStateCancelled: { 309 | print(" > HandleRequest() -> wrong response_state! (response_state:%i)", response_state); 310 | return; 311 | } 312 | } 313 | 314 | new response[2024]; 315 | grip_get_response_body_string(response, charsmax(response)); 316 | 317 | new GripHTTPStatus:status = grip_get_response_status_code(); 318 | if(status != GripHTTPStatusOk) { 319 | print(" > HandleRequest() -> wrong status! (status:%i). [response:'%s']", status, response); 320 | } 321 | 322 | new APIState_s: reqState = ReadPackCell(reqData); 323 | 324 | switch(reqState) { 325 | case m_Init: { 326 | Initialize(); 327 | } 328 | 329 | case m_RegisterBot: { 330 | new error[512]; 331 | new GripJSONValue: responseBody = grip_json_parse_response_body(error, charsmax(error)); 332 | if(responseBody == Invalid_GripJSONValue) { 333 | print("> HandleRequest() -> m_RegisterBot: Response parse error. ['%s']", error); 334 | // TODO: Need to prevent 335 | } 336 | 337 | new bool: is_bot = grip_json_object_get_bool(responseBody, "ok") && grip_json_object_get_bool(responseBody, "result.is_bot", .dot_not = true); 338 | if(is_bot) { 339 | new botName[64]; 340 | grip_json_object_get_string(responseBody, "result.first_name", botName, sizeof(botName), .dot_not = true); 341 | 342 | new botToken[64]; 343 | ReadPackString(reqData, botToken, charsmax(botToken)); 344 | 345 | new funcID = ReadPackCell(reqData); 346 | new pluginID = ReadPackCell(reqData); 347 | 348 | RegisterBot(botName, botToken, funcID, pluginID); 349 | } 350 | 351 | } 352 | case m_BotSubscriptions: { 353 | new error[512]; 354 | new GripJSONValue: responseBody = grip_json_parse_response_body(error, charsmax(error)); 355 | if(responseBody == Invalid_GripJSONValue) { 356 | print("> HandleRequest() -> m_BotSubscriptions: Response parse error. ['%s']", error); 357 | // TODO: Need to prevent 358 | } 359 | 360 | new bool: chat_found = grip_json_object_get_bool(responseBody, "ok"); 361 | if(chat_found) { 362 | new botID = ReadPackCell(reqData); 363 | new chatID[64]; 364 | ReadPackString(reqData, chatID, charsmax(chatID)); 365 | new funcID = ReadPackCell(reqData); 366 | new pluginID = ReadPackCell(reqData); 367 | 368 | new chatTitle[64]; 369 | //grip_json_object_get_string(responseBody, "result.title", chatTitle, charsmax(chatTitle), .dot_not = true); 370 | 371 | new chatType[32]; 372 | grip_json_object_get_string(responseBody, "result.type", chatType, charsmax(chatType), .dot_not = true); 373 | BotAddSubscription(botID, chatID, chatTitle, chatType, funcID, pluginID); 374 | } 375 | } 376 | case m_SendMessage: { 377 | new error[512]; 378 | new GripJSONValue: responseBody = grip_json_parse_response_body(error, charsmax(error)); 379 | if(responseBody == Invalid_GripJSONValue) { 380 | print("> HandleRequest() -> m_BotSubscriptions: Response parse error. ['%s']", error); 381 | // TODO: Need to prevent 382 | } 383 | } 384 | } 385 | 386 | //print(" > HandleRequest() -> response:'%s'", response); 387 | DestroyDataPack(reqData); 388 | } 389 | 390 | Initialize() { 391 | g_aBotsArray = ArrayCreate(_:BotData_s); 392 | 393 | g_bInitialized = true; 394 | 395 | g_fwAPI_Initialized = CreateMultiForward("TG_BotAPI_Initialized", ET_IGNORE); 396 | ExecuteForward(g_fwAPI_Initialized, g_fwResult); 397 | } 398 | 399 | RegisterBot(const botName[], const botToken[], const funcID, const pluginID) { 400 | new botData[BotData_s]; 401 | copy(botData[bot_Name], charsmax(botData[bot_Name]), botName); 402 | copy(botData[bot_Token], charsmax(botData[bot_Token]), botToken); 403 | botData[bot_Subscriptions] = ArrayCreate(64); 404 | 405 | ArrayPushArray(g_aBotsArray, botData); 406 | 407 | new botIndexEntry = ArraySize(g_aBotsArray); 408 | 409 | // public BotRegistred(const botID, const botName[]) { } 410 | if(callfunc_begin_i(funcID, pluginID) == 1) { 411 | callfunc_push_int(botIndexEntry); 412 | callfunc_push_str(botName, .copyback = false); 413 | callfunc_end(); 414 | } 415 | } 416 | 417 | BotAddSubscription(const botID, const chatID[], const chatTitle[], const chatType[], const funcID, const pluginID) { 418 | new botData[BotData_s]; 419 | ArrayGetArray(g_aBotsArray, botID, botData); 420 | 421 | ArrayPushString(botData[bot_Subscriptions], chatID); 422 | new subscriptionIndexEntry = ArraySize(botData[bot_Subscriptions]); 423 | 424 | ArrayPushArray(g_aBotsArray, botData); 425 | 426 | // public BotSubscriptionAdded(const subscriptionID, const botID, const chatID[], const chatTitle[], const chatType[]) { } 427 | if(callfunc_begin_i(funcID, pluginID) == 1) { 428 | callfunc_push_int(subscriptionIndexEntry - 1); 429 | callfunc_push_int(botID); 430 | callfunc_push_str(chatID, .copyback = false); 431 | callfunc_push_str(chatTitle, .copyback = false); 432 | callfunc_push_str(chatType, .copyback = false); 433 | callfunc_end(); 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /amxmodx/scripting/include/TelegramBotAPI.inc: -------------------------------------------------------------------------------- 1 | #if defined _telegram_bot_api_included 2 | #endinput 3 | #endif 4 | #define _telegram_bot_api_included 5 | 6 | #pragma reqlib telegram_bot_api 7 | #if !defined AMXMODX_NOAUTOLOAD 8 | #pragma loadlib telegram_bot_api 9 | #endif 10 | 11 | 12 | /* 13 | * CallBack: public BotRegistred(const botID, const botName[]) { } 14 | */ 15 | native TG_RegisterBot(const token[], const callBack[]); 16 | /* 17 | * CallBack: public BotSubscriptionAdded(const subscriptionID, const botID, const chatID, const chatTitle[], const chatType[]) { } 18 | */ 19 | native TG_BotAddSubscription(const botID, const chatID[], const callBack[]); 20 | /* 21 | * @noreturn 22 | */ 23 | native TG_BotSendMessageALL(const botID, const message[]); 24 | /* 25 | * @noreturn 26 | */ 27 | native TG_BotSendMessage(const botID, const chatUD[], const message[]); 28 | /* 29 | * return bool; 30 | */ 31 | native bool: TG_BotAPI_Allowed(); 32 | 33 | forward TG_BotAPI_Initialized(); 34 | --------------------------------------------------------------------------------