├── _config.yml ├── .gitignore ├── README.md ├── launch.sh ├── libs ├── redis.lua ├── JSON.lua └── lua-redis.lua ├── bot.lua └── tdcli.lua /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-tactile -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [GrandPlus](https://telegram.me/GrandPlus) 2 | 3 | # Developers! 4 | 5 | 6 | [mehran](https://github.com/mehmehran) ([Telegram](https://telegram.me/pythonscript)) 7 | * * * 8 | 9 | # Install source 10 | 11 | * * * 12 | cd $HOME 13 | 14 | git clone https://github.com/GrandTM/GrandPlus.git 15 | 16 | cd GrandPlus 17 | 18 | chmod +x launch.sh 19 | 20 | ./launch.sh install 21 | 22 | ./launch.sh 23 | 24 | # Enter a phone number | confirmation code. 25 | 26 | * * * 27 | 28 | ### Our channel: 29 | 30 | [GrandTeam](https://telegram.me/GrandTeam) 31 |   32 | -------------------------------------------------------------------------------- /launch.sh: -------------------------------------------------------------------------------- 1 | THIS_DIR=$(cd $(dirname $0); pwd) 2 | cd $THIS_DIR 3 | 4 | install() { 5 | wget "https://valtman.name/files/telegram-cli-1222" 6 | mv telegram-cli-1222 tg 7 | sudo chmod +x tg 8 | echo -e "———————————————————————————————————————————————————————————\n" 9 | echo -e " Write to launch a source command in the terminal ./launch.sh \n" 10 | echo -e "———————————————————————————————————————————————————————————\n" 11 | } 12 | 13 | if [ "$1" = "install" ]; then 14 | install 15 | else 16 | if [ ! -f ./tg ]; then 17 | echo "TG not found" 18 | echo "Run $0 install" 19 | exit 1 20 | fi 21 | ./tg -s bot.lua 22 | fi 23 | -------------------------------------------------------------------------------- /libs/redis.lua: -------------------------------------------------------------------------------- 1 | local Redis = (loadfile "./libs/lua-redis.lua")() 2 | --local FakeRedis = (loadfile "./tg/max/fakeredis.lua")() 3 | 4 | local params = { 5 | host = '127.0.0.1', 6 | port = 6379, 7 | } 8 | 9 | -- Overwrite HGETALL 10 | Redis.commands.hgetall = Redis.command('hgetall', { 11 | response = function(reply, command, ...) 12 | local new_reply = { } 13 | for i = 1, #reply, 2 do new_reply[reply[i]] = reply[i + 1] end 14 | return new_reply 15 | end 16 | }) 17 | 18 | local redis = nil 19 | 20 | -- Won't launch an error if fails 21 | local ok = pcall(function() 22 | redis = Redis.connect(params) 23 | end) 24 | 25 | if not ok then 26 | 27 | local fake_func = function() 28 | print('\27[31mCan\'t connect with Redis, install/configure it!\27[39m') 29 | end 30 | fake_func() 31 | fake = FakeRedis.new() 32 | 33 | print('\27[31mRedis addr: '..params.host..'\27[39m') 34 | print('\27[31mRedis port: '..params.port..'\27[39m') 35 | 36 | redis = setmetatable({fakeredis=true}, { 37 | __index = function(a, b) 38 | if b ~= 'data' and fake[b] then 39 | fake_func(b) 40 | end 41 | return fake[b] or fake_func 42 | end }) 43 | 44 | end 45 | 46 | 47 | return redis 48 | -------------------------------------------------------------------------------- /libs/JSON.lua: -------------------------------------------------------------------------------- 1 | -- -*- coding: utf-8 -*- 2 | -- 3 | -- Simple JSON encoding and decoding in pure Lua. 4 | -- 5 | -- Copyright 2010-2014 Jeffrey Friedl 6 | -- http://regex.info/blog/ 7 | -- 8 | -- Latest version: http://regex.info/blog/lua/json 9 | -- 10 | -- This code is released under a Creative Commons CC-BY "Attribution" License: 11 | -- http://creativecommons.org/licenses/by/3.0/deed.en_US 12 | -- 13 | -- It can be used for any purpose so long as the copyright notice above, 14 | -- the web-page links above, and the 'AUTHOR_NOTE' string below are 15 | -- maintained. Enjoy. 16 | -- 17 | local VERSION = 20141223.14 -- version history at end of file 18 | local AUTHOR_NOTE = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 20141223.14 ]-" 19 | 20 | -- 21 | -- The 'AUTHOR_NOTE' variable exists so that information about the source 22 | -- of the package is maintained even in compiled versions. It's also 23 | -- included in OBJDEF below mostly to quiet warnings about unused variables. 24 | -- 25 | local OBJDEF = { 26 | VERSION = VERSION, 27 | AUTHOR_NOTE = AUTHOR_NOTE, 28 | } 29 | 30 | 31 | -- 32 | -- Simple JSON encoding and decoding in pure Lua. 33 | -- http://www.json.org/ 34 | -- 35 | -- 36 | -- JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines 37 | -- 38 | -- local lua_value = JSON:decode(raw_json_text) 39 | -- 40 | -- local raw_json_text = JSON:encode(lua_table_or_value) 41 | -- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability 42 | -- 43 | -- 44 | -- 45 | -- DECODING (from a JSON string to a Lua table) 46 | -- 47 | -- 48 | -- JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines 49 | -- 50 | -- local lua_value = JSON:decode(raw_json_text) 51 | -- 52 | -- If the JSON text is for an object or an array, e.g. 53 | -- { "what": "books", "count": 3 } 54 | -- or 55 | -- [ "Larry", "Curly", "Moe" ] 56 | -- 57 | -- the result is a Lua table, e.g. 58 | -- { what = "books", count = 3 } 59 | -- or 60 | -- { "Larry", "Curly", "Moe" } 61 | -- 62 | -- 63 | -- The encode and decode routines accept an optional second argument, 64 | -- "etc", which is not used during encoding or decoding, but upon error 65 | -- is passed along to error handlers. It can be of any type (including nil). 66 | -- 67 | -- 68 | -- 69 | -- ERROR HANDLING 70 | -- 71 | -- With most errors during decoding, this code calls 72 | -- 73 | -- JSON:onDecodeError(message, text, location, etc) 74 | -- 75 | -- with a message about the error, and if known, the JSON text being 76 | -- parsed and the byte count where the problem was discovered. You can 77 | -- replace the default JSON:onDecodeError() with your own function. 78 | -- 79 | -- The default onDecodeError() merely augments the message with data 80 | -- about the text and the location if known (and if a second 'etc' 81 | -- argument had been provided to decode(), its value is tacked onto the 82 | -- message as well), and then calls JSON.assert(), which itself defaults 83 | -- to Lua's built-in assert(), and can also be overridden. 84 | -- 85 | -- For example, in an Adobe Lightroom plugin, you might use something like 86 | -- 87 | -- function JSON:onDecodeError(message, text, location, etc) 88 | -- LrErrors.throwUserError("Internal Error: invalid JSON data") 89 | -- end 90 | -- 91 | -- or even just 92 | -- 93 | -- function JSON.assert(message) 94 | -- LrErrors.throwUserError("Internal Error: " .. message) 95 | -- end 96 | -- 97 | -- If JSON:decode() is passed a nil, this is called instead: 98 | -- 99 | -- JSON:onDecodeOfNilError(message, nil, nil, etc) 100 | -- 101 | -- and if JSON:decode() is passed HTML instead of JSON, this is called: 102 | -- 103 | -- JSON:onDecodeOfHTMLError(message, text, nil, etc) 104 | -- 105 | -- The use of the fourth 'etc' argument allows stronger coordination 106 | -- between decoding and error reporting, especially when you provide your 107 | -- own error-handling routines. Continuing with the the Adobe Lightroom 108 | -- plugin example: 109 | -- 110 | -- function JSON:onDecodeError(message, text, location, etc) 111 | -- local note = "Internal Error: invalid JSON data" 112 | -- if type(etc) = 'table' and etc.photo then 113 | -- note = note .. " while processing for " .. etc.photo:getFormattedMetadata('fileName') 114 | -- end 115 | -- LrErrors.throwUserError(note) 116 | -- end 117 | -- 118 | -- : 119 | -- : 120 | -- 121 | -- for i, photo in ipairs(photosToProcess) do 122 | -- : 123 | -- : 124 | -- local data = JSON:decode(someJsonText, { photo = photo }) 125 | -- : 126 | -- : 127 | -- end 128 | -- 129 | -- 130 | -- 131 | -- 132 | -- 133 | -- DECODING AND STRICT TYPES 134 | -- 135 | -- Because both JSON objects and JSON arrays are converted to Lua tables, 136 | -- it's not normally possible to tell which original JSON type a 137 | -- particular Lua table was derived from, or guarantee decode-encode 138 | -- round-trip equivalency. 139 | -- 140 | -- However, if you enable strictTypes, e.g. 141 | -- 142 | -- JSON = assert(loadfile "JSON.lua")() --load the routines 143 | -- JSON.strictTypes = true 144 | -- 145 | -- then the Lua table resulting from the decoding of a JSON object or 146 | -- JSON array is marked via Lua metatable, so that when re-encoded with 147 | -- JSON:encode() it ends up as the appropriate JSON type. 148 | -- 149 | -- (This is not the default because other routines may not work well with 150 | -- tables that have a metatable set, for example, Lightroom API calls.) 151 | -- 152 | -- 153 | -- ENCODING (from a lua table to a JSON string) 154 | -- 155 | -- JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines 156 | -- 157 | -- local raw_json_text = JSON:encode(lua_table_or_value) 158 | -- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability 159 | -- local custom_pretty = JSON:encode(lua_table_or_value, etc, { pretty = true, indent = "| ", align_keys = false }) 160 | -- 161 | -- On error during encoding, this code calls: 162 | -- 163 | -- JSON:onEncodeError(message, etc) 164 | -- 165 | -- which you can override in your local JSON object. 166 | -- 167 | -- The 'etc' in the error call is the second argument to encode() 168 | -- and encode_pretty(), or nil if it wasn't provided. 169 | -- 170 | -- 171 | -- PRETTY-PRINTING 172 | -- 173 | -- An optional third argument, a table of options, allows a bit of 174 | -- configuration about how the encoding takes place: 175 | -- 176 | -- pretty = JSON:encode(val, etc, { 177 | -- pretty = true, -- if false, no other options matter 178 | -- indent = " ", -- this provides for a three-space indent per nesting level 179 | -- align_keys = false, -- see below 180 | -- }) 181 | -- 182 | -- encode() and encode_pretty() are identical except that encode_pretty() 183 | -- provides a default options table if none given in the call: 184 | -- 185 | -- { pretty = true, align_keys = false, indent = " " } 186 | -- 187 | -- For example, if 188 | -- 189 | -- JSON:encode(data) 190 | -- 191 | -- produces: 192 | -- 193 | -- {"city":"Kyoto","climate":{"avg_temp":16,"humidity":"high","snowfall":"minimal"},"country":"Japan","wards":11} 194 | -- 195 | -- then 196 | -- 197 | -- JSON:encode_pretty(data) 198 | -- 199 | -- produces: 200 | -- 201 | -- { 202 | -- "city": "Kyoto", 203 | -- "climate": { 204 | -- "avg_temp": 16, 205 | -- "humidity": "high", 206 | -- "snowfall": "minimal" 207 | -- }, 208 | -- "country": "Japan", 209 | -- "wards": 11 210 | -- } 211 | -- 212 | -- The following three lines return identical results: 213 | -- JSON:encode_pretty(data) 214 | -- JSON:encode_pretty(data, nil, { pretty = true, align_keys = false, indent = " " }) 215 | -- JSON:encode (data, nil, { pretty = true, align_keys = false, indent = " " }) 216 | -- 217 | -- An example of setting your own indent string: 218 | -- 219 | -- JSON:encode_pretty(data, nil, { pretty = true, indent = "| " }) 220 | -- 221 | -- produces: 222 | -- 223 | -- { 224 | -- | "city": "Kyoto", 225 | -- | "climate": { 226 | -- | | "avg_temp": 16, 227 | -- | | "humidity": "high", 228 | -- | | "snowfall": "minimal" 229 | -- | }, 230 | -- | "country": "Japan", 231 | -- | "wards": 11 232 | -- } 233 | -- 234 | -- An example of setting align_keys to true: 235 | -- 236 | -- JSON:encode_pretty(data, nil, { pretty = true, indent = " ", align_keys = true }) 237 | -- 238 | -- produces: 239 | -- 240 | -- { 241 | -- "city": "Kyoto", 242 | -- "climate": { 243 | -- "avg_temp": 16, 244 | -- "humidity": "high", 245 | -- "snowfall": "minimal" 246 | -- }, 247 | -- "country": "Japan", 248 | -- "wards": 11 249 | -- } 250 | -- 251 | -- which I must admit is kinda ugly, sorry. This was the default for 252 | -- encode_pretty() prior to version 20141223.14. 253 | -- 254 | -- 255 | -- AMBIGUOUS SITUATIONS DURING THE ENCODING 256 | -- 257 | -- During the encode, if a Lua table being encoded contains both string 258 | -- and numeric keys, it fits neither JSON's idea of an object, nor its 259 | -- idea of an array. To get around this, when any string key exists (or 260 | -- when non-positive numeric keys exist), numeric keys are converted to 261 | -- strings. 262 | -- 263 | -- For example, 264 | -- JSON:encode({ "one", "two", "three", SOMESTRING = "some string" })) 265 | -- produces the JSON object 266 | -- {"1":"one","2":"two","3":"three","SOMESTRING":"some string"} 267 | -- 268 | -- To prohibit this conversion and instead make it an error condition, set 269 | -- JSON.noKeyConversion = true 270 | -- 271 | 272 | 273 | 274 | 275 | -- 276 | -- SUMMARY OF METHODS YOU CAN OVERRIDE IN YOUR LOCAL LUA JSON OBJECT 277 | -- 278 | -- assert 279 | -- onDecodeError 280 | -- onDecodeOfNilError 281 | -- onDecodeOfHTMLError 282 | -- onEncodeError 283 | -- 284 | -- If you want to create a separate Lua JSON object with its own error handlers, 285 | -- you can reload JSON.lua or use the :new() method. 286 | -- 287 | --------------------------------------------------------------------------- 288 | 289 | local default_pretty_indent = " " 290 | local default_pretty_options = { pretty = true, align_keys = false, indent = default_pretty_indent } 291 | 292 | local isArray = { __tostring = function() return "JSON array" end } isArray.__index = isArray 293 | local isObject = { __tostring = function() return "JSON object" end } isObject.__index = isObject 294 | 295 | 296 | function OBJDEF:newArray(tbl) 297 | return setmetatable(tbl or {}, isArray) 298 | end 299 | 300 | function OBJDEF:newObject(tbl) 301 | return setmetatable(tbl or {}, isObject) 302 | end 303 | 304 | local function unicode_codepoint_as_utf8(codepoint) 305 | -- 306 | -- codepoint is a number 307 | -- 308 | if codepoint <= 127 then 309 | return string.char(codepoint) 310 | 311 | elseif codepoint <= 2047 then 312 | -- 313 | -- 110yyyxx 10xxxxxx <-- useful notation from http://en.wikipedia.org/wiki/Utf8 314 | -- 315 | local highpart = math.floor(codepoint / 0x40) 316 | local lowpart = codepoint - (0x40 * highpart) 317 | return string.char(0xC0 + highpart, 318 | 0x80 + lowpart) 319 | 320 | elseif codepoint <= 65535 then 321 | -- 322 | -- 1110yyyy 10yyyyxx 10xxxxxx 323 | -- 324 | local highpart = math.floor(codepoint / 0x1000) 325 | local remainder = codepoint - 0x1000 * highpart 326 | local midpart = math.floor(remainder / 0x40) 327 | local lowpart = remainder - 0x40 * midpart 328 | 329 | highpart = 0xE0 + highpart 330 | midpart = 0x80 + midpart 331 | lowpart = 0x80 + lowpart 332 | 333 | -- 334 | -- Check for an invalid character (thanks Andy R. at Adobe). 335 | -- See table 3.7, page 93, in http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf#G28070 336 | -- 337 | if ( highpart == 0xE0 and midpart < 0xA0 ) or 338 | ( highpart == 0xED and midpart > 0x9F ) or 339 | ( highpart == 0xF0 and midpart < 0x90 ) or 340 | ( highpart == 0xF4 and midpart > 0x8F ) 341 | then 342 | return "?" 343 | else 344 | return string.char(highpart, 345 | midpart, 346 | lowpart) 347 | end 348 | 349 | else 350 | -- 351 | -- 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx 352 | -- 353 | local highpart = math.floor(codepoint / 0x40000) 354 | local remainder = codepoint - 0x40000 * highpart 355 | local midA = math.floor(remainder / 0x1000) 356 | remainder = remainder - 0x1000 * midA 357 | local midB = math.floor(remainder / 0x40) 358 | local lowpart = remainder - 0x40 * midB 359 | 360 | return string.char(0xF0 + highpart, 361 | 0x80 + midA, 362 | 0x80 + midB, 363 | 0x80 + lowpart) 364 | end 365 | end 366 | 367 | function OBJDEF:onDecodeError(message, text, location, etc) 368 | if text then 369 | if location then 370 | message = string.format("%s at char %d of: %s", message, location, text) 371 | else 372 | message = string.format("%s: %s", message, text) 373 | end 374 | end 375 | 376 | if etc ~= nil then 377 | message = message .. " (" .. OBJDEF:encode(etc) .. ")" 378 | end 379 | 380 | if self.assert then 381 | self.assert(false, message) 382 | else 383 | assert(false, message) 384 | end 385 | end 386 | 387 | OBJDEF.onDecodeOfNilError = OBJDEF.onDecodeError 388 | OBJDEF.onDecodeOfHTMLError = OBJDEF.onDecodeError 389 | 390 | function OBJDEF:onEncodeError(message, etc) 391 | if etc ~= nil then 392 | message = message .. " (" .. OBJDEF:encode(etc) .. ")" 393 | end 394 | 395 | if self.assert then 396 | self.assert(false, message) 397 | else 398 | assert(false, message) 399 | end 400 | end 401 | 402 | local function grok_number(self, text, start, etc) 403 | -- 404 | -- Grab the integer part 405 | -- 406 | local integer_part = text:match('^-?[1-9]%d*', start) 407 | or text:match("^-?0", start) 408 | 409 | if not integer_part then 410 | self:onDecodeError("expected number", text, start, etc) 411 | end 412 | 413 | local i = start + integer_part:len() 414 | 415 | -- 416 | -- Grab an optional decimal part 417 | -- 418 | local decimal_part = text:match('^%.%d+', i) or "" 419 | 420 | i = i + decimal_part:len() 421 | 422 | -- 423 | -- Grab an optional exponential part 424 | -- 425 | local exponent_part = text:match('^[eE][-+]?%d+', i) or "" 426 | 427 | i = i + exponent_part:len() 428 | 429 | local full_number_text = integer_part .. decimal_part .. exponent_part 430 | local as_number = tonumber(full_number_text) 431 | 432 | if not as_number then 433 | self:onDecodeError("bad number", text, start, etc) 434 | end 435 | 436 | return as_number, i 437 | end 438 | 439 | 440 | local function grok_string(self, text, start, etc) 441 | 442 | if text:sub(start,start) ~= '"' then 443 | self:onDecodeError("expected string's opening quote", text, start, etc) 444 | end 445 | 446 | local i = start + 1 -- +1 to bypass the initial quote 447 | local text_len = text:len() 448 | local VALUE = "" 449 | while i <= text_len do 450 | local c = text:sub(i,i) 451 | if c == '"' then 452 | return VALUE, i + 1 453 | end 454 | if c ~= '\\' then 455 | VALUE = VALUE .. c 456 | i = i + 1 457 | elseif text:match('^\\b', i) then 458 | VALUE = VALUE .. "\b" 459 | i = i + 2 460 | elseif text:match('^\\f', i) then 461 | VALUE = VALUE .. "\f" 462 | i = i + 2 463 | elseif text:match('^\\n', i) then 464 | VALUE = VALUE .. "\n" 465 | i = i + 2 466 | elseif text:match('^\\r', i) then 467 | VALUE = VALUE .. "\r" 468 | i = i + 2 469 | elseif text:match('^\\t', i) then 470 | VALUE = VALUE .. "\t" 471 | i = i + 2 472 | else 473 | local hex = text:match('^\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) 474 | if hex then 475 | i = i + 6 -- bypass what we just read 476 | 477 | -- We have a Unicode codepoint. It could be standalone, or if in the proper range and 478 | -- followed by another in a specific range, it'll be a two-code surrogate pair. 479 | local codepoint = tonumber(hex, 16) 480 | if codepoint >= 0xD800 and codepoint <= 0xDBFF then 481 | -- it's a hi surrogate... see whether we have a following low 482 | local lo_surrogate = text:match('^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) 483 | if lo_surrogate then 484 | i = i + 6 -- bypass the low surrogate we just read 485 | codepoint = 0x2400 + (codepoint - 0xD800) * 0x400 + tonumber(lo_surrogate, 16) 486 | else 487 | -- not a proper low, so we'll just leave the first codepoint as is and spit it out. 488 | end 489 | end 490 | VALUE = VALUE .. unicode_codepoint_as_utf8(codepoint) 491 | 492 | else 493 | 494 | -- just pass through what's escaped 495 | VALUE = VALUE .. text:match('^\\(.)', i) 496 | i = i + 2 497 | end 498 | end 499 | end 500 | 501 | self:onDecodeError("unclosed string", text, start, etc) 502 | end 503 | 504 | local function skip_whitespace(text, start) 505 | 506 | local _, match_end = text:find("^[ \n\r\t]+", start) -- [http://www.ietf.org/rfc/rfc4627.txt] Section 2 507 | if match_end then 508 | return match_end + 1 509 | else 510 | return start 511 | end 512 | end 513 | 514 | local grok_one -- assigned later 515 | 516 | local function grok_object(self, text, start, etc) 517 | if text:sub(start,start) ~= '{' then 518 | self:onDecodeError("expected '{'", text, start, etc) 519 | end 520 | 521 | local i = skip_whitespace(text, start + 1) -- +1 to skip the '{' 522 | 523 | local VALUE = self.strictTypes and self:newObject { } or { } 524 | 525 | if text:sub(i,i) == '}' then 526 | return VALUE, i + 1 527 | end 528 | local text_len = text:len() 529 | while i <= text_len do 530 | local key, new_i = grok_string(self, text, i, etc) 531 | 532 | i = skip_whitespace(text, new_i) 533 | 534 | if text:sub(i, i) ~= ':' then 535 | self:onDecodeError("expected colon", text, i, etc) 536 | end 537 | 538 | i = skip_whitespace(text, i + 1) 539 | 540 | local new_val, new_i = grok_one(self, text, i) 541 | 542 | VALUE[key] = new_val 543 | 544 | -- 545 | -- Expect now either '}' to end things, or a ',' to allow us to continue. 546 | -- 547 | i = skip_whitespace(text, new_i) 548 | 549 | local c = text:sub(i,i) 550 | 551 | if c == '}' then 552 | return VALUE, i + 1 553 | end 554 | 555 | if text:sub(i, i) ~= ',' then 556 | self:onDecodeError("expected comma or '}'", text, i, etc) 557 | end 558 | 559 | i = skip_whitespace(text, i + 1) 560 | end 561 | 562 | self:onDecodeError("unclosed '{'", text, start, etc) 563 | end 564 | 565 | local function grok_array(self, text, start, etc) 566 | if text:sub(start,start) ~= '[' then 567 | self:onDecodeError("expected '['", text, start, etc) 568 | end 569 | 570 | local i = skip_whitespace(text, start + 1) -- +1 to skip the '[' 571 | local VALUE = self.strictTypes and self:newArray { } or { } 572 | if text:sub(i,i) == ']' then 573 | return VALUE, i + 1 574 | end 575 | 576 | local VALUE_INDEX = 1 577 | 578 | local text_len = text:len() 579 | while i <= text_len do 580 | local val, new_i = grok_one(self, text, i) 581 | 582 | -- can't table.insert(VALUE, val) here because it's a no-op if val is nil 583 | VALUE[VALUE_INDEX] = val 584 | VALUE_INDEX = VALUE_INDEX + 1 585 | 586 | i = skip_whitespace(text, new_i) 587 | 588 | -- 589 | -- Expect now either ']' to end things, or a ',' to allow us to continue. 590 | -- 591 | local c = text:sub(i,i) 592 | if c == ']' then 593 | return VALUE, i + 1 594 | end 595 | if text:sub(i, i) ~= ',' then 596 | self:onDecodeError("expected comma or '['", text, i, etc) 597 | end 598 | i = skip_whitespace(text, i + 1) 599 | end 600 | self:onDecodeError("unclosed '['", text, start, etc) 601 | end 602 | 603 | 604 | grok_one = function(self, text, start, etc) 605 | -- Skip any whitespace 606 | start = skip_whitespace(text, start) 607 | 608 | if start > text:len() then 609 | self:onDecodeError("unexpected end of string", text, nil, etc) 610 | end 611 | 612 | if text:find('^"', start) then 613 | return grok_string(self, text, start, etc) 614 | 615 | elseif text:find('^[-0123456789 ]', start) then 616 | return grok_number(self, text, start, etc) 617 | 618 | elseif text:find('^%{', start) then 619 | return grok_object(self, text, start, etc) 620 | 621 | elseif text:find('^%[', start) then 622 | return grok_array(self, text, start, etc) 623 | 624 | elseif text:find('^true', start) then 625 | return true, start + 4 626 | 627 | elseif text:find('^false', start) then 628 | return false, start + 5 629 | 630 | elseif text:find('^null', start) then 631 | return nil, start + 4 632 | 633 | else 634 | self:onDecodeError("can't parse JSON", text, start, etc) 635 | end 636 | end 637 | 638 | function OBJDEF:decode(text, etc) 639 | if type(self) ~= 'table' or self.__index ~= OBJDEF then 640 | OBJDEF:onDecodeError("JSON:decode must be called in method format", nil, nil, etc) 641 | end 642 | 643 | if text == nil then 644 | self:onDecodeOfNilError(string.format("nil passed to JSON:decode()"), nil, nil, etc) 645 | elseif type(text) ~= 'string' then 646 | self:onDecodeError(string.format("expected string argument to JSON:decode(), got %s", type(text)), nil, nil, etc) 647 | end 648 | 649 | if text:match('^%s*$') then 650 | return nil 651 | end 652 | 653 | if text:match('^%s*<') then 654 | -- Can't be JSON... we'll assume it's HTML 655 | self:onDecodeOfHTMLError(string.format("html passed to JSON:decode()"), text, nil, etc) 656 | end 657 | 658 | -- 659 | -- Ensure that it's not UTF-32 or UTF-16. 660 | -- Those are perfectly valid encodings for JSON (as per RFC 4627 section 3), 661 | -- but this package can't handle them. 662 | -- 663 | if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() == 0) then 664 | self:onDecodeError("JSON package groks only UTF-8, sorry", text, nil, etc) 665 | end 666 | 667 | local success, value = pcall(grok_one, self, text, 1, etc) 668 | 669 | if success then 670 | return value 671 | else 672 | -- if JSON:onDecodeError() didn't abort out of the pcall, we'll have received the error message here as "value", so pass it along as an assert. 673 | if self.assert then 674 | self.assert(false, value) 675 | else 676 | assert(false, value) 677 | end 678 | -- and if we're still here, return a nil and throw the error message on as a second arg 679 | return nil, value 680 | end 681 | end 682 | 683 | local function backslash_replacement_function(c) 684 | if c == "\n" then 685 | return "\\n" 686 | elseif c == "\r" then 687 | return "\\r" 688 | elseif c == "\t" then 689 | return "\\t" 690 | elseif c == "\b" then 691 | return "\\b" 692 | elseif c == "\f" then 693 | return "\\f" 694 | elseif c == '"' then 695 | return '\\"' 696 | elseif c == '\\' then 697 | return '\\\\' 698 | else 699 | return string.format("\\u%04x", c:byte()) 700 | end 701 | end 702 | 703 | local chars_to_be_escaped_in_JSON_string 704 | = '[' 705 | .. '"' -- class sub-pattern to match a double quote 706 | .. '%\\' -- class sub-pattern to match a backslash 707 | .. '%z' -- class sub-pattern to match a null 708 | .. '\001' .. '-' .. '\031' -- class sub-pattern to match control characters 709 | .. ']' 710 | 711 | local function json_string_literal(value) 712 | local newval = value:gsub(chars_to_be_escaped_in_JSON_string, backslash_replacement_function) 713 | return '"' .. newval .. '"' 714 | end 715 | 716 | local function object_or_array(self, T, etc) 717 | -- 718 | -- We need to inspect all the keys... if there are any strings, we'll convert to a JSON 719 | -- object. If there are only numbers, it's a JSON array. 720 | -- 721 | -- If we'll be converting to a JSON object, we'll want to sort the keys so that the 722 | -- end result is deterministic. 723 | -- 724 | local string_keys = { } 725 | local number_keys = { } 726 | local number_keys_must_be_strings = false 727 | local maximum_number_key 728 | 729 | for key in pairs(T) do 730 | if type(key) == 'string' then 731 | table.insert(string_keys, key) 732 | elseif type(key) == 'number' then 733 | table.insert(number_keys, key) 734 | if key <= 0 or key >= math.huge then 735 | number_keys_must_be_strings = true 736 | elseif not maximum_number_key or key > maximum_number_key then 737 | maximum_number_key = key 738 | end 739 | else 740 | self:onEncodeError("can't encode table with a key of type " .. type(key), etc) 741 | end 742 | end 743 | 744 | if #string_keys == 0 and not number_keys_must_be_strings then 745 | -- 746 | -- An empty table, or a numeric-only array 747 | -- 748 | if #number_keys > 0 then 749 | return nil, maximum_number_key -- an array 750 | elseif tostring(T) == "JSON array" then 751 | return nil 752 | elseif tostring(T) == "JSON object" then 753 | return { } 754 | else 755 | -- have to guess, so we'll pick array, since empty arrays are likely more common than empty objects 756 | return nil 757 | end 758 | end 759 | 760 | table.sort(string_keys) 761 | 762 | local map 763 | if #number_keys > 0 then 764 | -- 765 | -- If we're here then we have either mixed string/number keys, or numbers inappropriate for a JSON array 766 | -- It's not ideal, but we'll turn the numbers into strings so that we can at least create a JSON object. 767 | -- 768 | 769 | if self.noKeyConversion then 770 | self:onEncodeError("a table with both numeric and string keys could be an object or array; aborting", etc) 771 | end 772 | 773 | -- 774 | -- Have to make a shallow copy of the source table so we can remap the numeric keys to be strings 775 | -- 776 | map = { } 777 | for key, val in pairs(T) do 778 | map[key] = val 779 | end 780 | 781 | table.sort(number_keys) 782 | 783 | -- 784 | -- Throw numeric keys in there as strings 785 | -- 786 | for _, number_key in ipairs(number_keys) do 787 | local string_key = tostring(number_key) 788 | if map[string_key] == nil then 789 | table.insert(string_keys , string_key) 790 | map[string_key] = T[number_key] 791 | else 792 | self:onEncodeError("conflict converting table with mixed-type keys into a JSON object: key " .. number_key .. " exists both as a string and a number.", etc) 793 | end 794 | end 795 | end 796 | 797 | return string_keys, nil, map 798 | end 799 | 800 | -- 801 | -- Encode 802 | -- 803 | -- 'options' is nil, or a table with possible keys: 804 | -- pretty -- if true, return a pretty-printed version 805 | -- indent -- a string (usually of spaces) used to indent each nested level 806 | -- align_keys -- if true, align all the keys when formatting a table 807 | -- 808 | local encode_value -- must predeclare because it calls itself 809 | function encode_value(self, value, parents, etc, options, indent) 810 | 811 | if value == nil then 812 | return 'null' 813 | 814 | elseif type(value) == 'string' then 815 | return json_string_literal(value) 816 | 817 | elseif type(value) == 'number' then 818 | if value ~= value then 819 | -- 820 | -- NaN (Not a Number). 821 | -- JSON has no NaN, so we have to fudge the best we can. This should really be a package option. 822 | -- 823 | return "null" 824 | elseif value >= math.huge then 825 | -- 826 | -- Positive infinity. JSON has no INF, so we have to fudge the best we can. This should 827 | -- really be a package option. Note: at least with some implementations, positive infinity 828 | -- is both ">= math.huge" and "<= -math.huge", which makes no sense but that's how it is. 829 | -- Negative infinity is properly "<= -math.huge". So, we must be sure to check the ">=" 830 | -- case first. 831 | -- 832 | return "1e+9999" 833 | elseif value <= -math.huge then 834 | -- 835 | -- Negative infinity. 836 | -- JSON has no INF, so we have to fudge the best we can. This should really be a package option. 837 | -- 838 | return "-1e+9999" 839 | else 840 | return tostring(value) 841 | end 842 | 843 | elseif type(value) == 'boolean' then 844 | return tostring(value) 845 | 846 | elseif type(value) ~= 'table' then 847 | self:onEncodeError("can't convert " .. type(value) .. " to JSON", etc) 848 | 849 | else 850 | -- 851 | -- A table to be converted to either a JSON object or array. 852 | -- 853 | local T = value 854 | 855 | if type(options) ~= 'table' then 856 | options = {} 857 | end 858 | if type(indent) ~= 'string' then 859 | indent = "" 860 | end 861 | 862 | if parents[T] then 863 | self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc) 864 | else 865 | parents[T] = true 866 | end 867 | 868 | local result_value 869 | 870 | local object_keys, maximum_number_key, map = object_or_array(self, T, etc) 871 | if maximum_number_key then 872 | -- 873 | -- An array... 874 | -- 875 | local ITEMS = { } 876 | for i = 1, maximum_number_key do 877 | table.insert(ITEMS, encode_value(self, T[i], parents, etc, options, indent)) 878 | end 879 | 880 | if options.pretty then 881 | result_value = "[ " .. table.concat(ITEMS, ", ") .. " ]" 882 | else 883 | result_value = "[" .. table.concat(ITEMS, ",") .. "]" 884 | end 885 | 886 | elseif object_keys then 887 | -- 888 | -- An object 889 | -- 890 | local TT = map or T 891 | 892 | if options.pretty then 893 | 894 | local KEYS = { } 895 | local max_key_length = 0 896 | for _, key in ipairs(object_keys) do 897 | local encoded = encode_value(self, tostring(key), parents, etc, options, indent) 898 | if options.align_keys then 899 | max_key_length = math.max(max_key_length, #encoded) 900 | end 901 | table.insert(KEYS, encoded) 902 | end 903 | local key_indent = indent .. tostring(options.indent or "") 904 | local subtable_indent = key_indent .. string.rep(" ", max_key_length) .. (options.align_keys and " " or "") 905 | local FORMAT = "%s%" .. string.format("%d", max_key_length) .. "s: %s" 906 | 907 | local COMBINED_PARTS = { } 908 | for i, key in ipairs(object_keys) do 909 | local encoded_val = encode_value(self, TT[key], parents, etc, options, subtable_indent) 910 | table.insert(COMBINED_PARTS, string.format(FORMAT, key_indent, KEYS[i], encoded_val)) 911 | end 912 | result_value = "{\n" .. table.concat(COMBINED_PARTS, ",\n") .. "\n" .. indent .. "}" 913 | 914 | else 915 | 916 | local PARTS = { } 917 | for _, key in ipairs(object_keys) do 918 | local encoded_val = encode_value(self, TT[key], parents, etc, options, indent) 919 | local encoded_key = encode_value(self, tostring(key), parents, etc, options, indent) 920 | table.insert(PARTS, string.format("%s:%s", encoded_key, encoded_val)) 921 | end 922 | result_value = "{" .. table.concat(PARTS, ",") .. "}" 923 | 924 | end 925 | else 926 | -- 927 | -- An empty array/object... we'll treat it as an array, though it should really be an option 928 | -- 929 | result_value = "[]" 930 | end 931 | 932 | parents[T] = false 933 | return result_value 934 | end 935 | end 936 | 937 | 938 | function OBJDEF:encode(value, etc, options) 939 | if type(self) ~= 'table' or self.__index ~= OBJDEF then 940 | OBJDEF:onEncodeError("JSON:encode must be called in method format", etc) 941 | end 942 | return encode_value(self, value, {}, etc, options or nil) 943 | end 944 | 945 | function OBJDEF:encode_pretty(value, etc, options) 946 | if type(self) ~= 'table' or self.__index ~= OBJDEF then 947 | OBJDEF:onEncodeError("JSON:encode_pretty must be called in method format", etc) 948 | end 949 | return encode_value(self, value, {}, etc, options or default_pretty_options) 950 | end 951 | 952 | function OBJDEF.__tostring() 953 | return "JSON encode/decode package" 954 | end 955 | 956 | OBJDEF.__index = OBJDEF 957 | 958 | function OBJDEF:new(args) 959 | local new = { } 960 | 961 | if args then 962 | for key, val in pairs(args) do 963 | new[key] = val 964 | end 965 | end 966 | 967 | return setmetatable(new, OBJDEF) 968 | end 969 | 970 | return OBJDEF:new() 971 | 972 | -- 973 | -- Version history: 974 | -- 975 | -- 20141223.14 The encode_pretty() routine produced fine results for small datasets, but isn't really 976 | -- appropriate for anything large, so with help from Alex Aulbach I've made the encode routines 977 | -- more flexible, and changed the default encode_pretty() to be more generally useful. 978 | -- 979 | -- Added a third 'options' argument to the encode() and encode_pretty() routines, to control 980 | -- how the encoding takes place. 981 | -- 982 | -- Updated docs to add assert() call to the loadfile() line, just as good practice so that 983 | -- if there is a problem loading JSON.lua, the appropriate error message will percolate up. 984 | -- 985 | -- 20140920.13 Put back (in a way that doesn't cause warnings about unused variables) the author string, 986 | -- so that the source of the package, and its version number, are visible in compiled copies. 987 | -- 988 | -- 20140911.12 Minor lua cleanup. 989 | -- Fixed internal reference to 'JSON.noKeyConversion' to reference 'self' instead of 'JSON'. 990 | -- (Thanks to SmugMug's David Parry for these.) 991 | -- 992 | -- 20140418.11 JSON nulls embedded within an array were being ignored, such that 993 | -- ["1",null,null,null,null,null,"seven"], 994 | -- would return 995 | -- {1,"seven"} 996 | -- It's now fixed to properly return 997 | -- {1, nil, nil, nil, nil, nil, "seven"} 998 | -- Thanks to "haddock" for catching the error. 999 | -- 1000 | -- 20140116.10 The user's JSON.assert() wasn't always being used. Thanks to "blue" for the heads up. 1001 | -- 1002 | -- 20131118.9 Update for Lua 5.3... it seems that tostring(2/1) produces "2.0" instead of "2", 1003 | -- and this caused some problems. 1004 | -- 1005 | -- 20131031.8 Unified the code for encode() and encode_pretty(); they had been stupidly separate, 1006 | -- and had of course diverged (encode_pretty didn't get the fixes that encode got, so 1007 | -- sometimes produced incorrect results; thanks to Mattie for the heads up). 1008 | -- 1009 | -- Handle encoding tables with non-positive numeric keys (unlikely, but possible). 1010 | -- 1011 | -- If a table has both numeric and string keys, or its numeric keys are inappropriate 1012 | -- (such as being non-positive or infinite), the numeric keys are turned into 1013 | -- string keys appropriate for a JSON object. So, as before, 1014 | -- JSON:encode({ "one", "two", "three" }) 1015 | -- produces the array 1016 | -- ["one","two","three"] 1017 | -- but now something with mixed key types like 1018 | -- JSON:encode({ "one", "two", "three", SOMESTRING = "some string" })) 1019 | -- instead of throwing an error produces an object: 1020 | -- {"1":"one","2":"two","3":"three","SOMESTRING":"some string"} 1021 | -- 1022 | -- To maintain the prior throw-an-error semantics, set 1023 | -- JSON.noKeyConversion = true 1024 | -- 1025 | -- 20131004.7 Release under a Creative Commons CC-BY license, which I should have done from day one, sorry. 1026 | -- 1027 | -- 20130120.6 Comment update: added a link to the specific page on my blog where this code can 1028 | -- be found, so that folks who come across the code outside of my blog can find updates 1029 | -- more easily. 1030 | -- 1031 | -- 20111207.5 Added support for the 'etc' arguments, for better error reporting. 1032 | -- 1033 | -- 20110731.4 More feedback from David Kolf on how to make the tests for Nan/Infinity system independent. 1034 | -- 1035 | -- 20110730.3 Incorporated feedback from David Kolf at http://lua-users.org/wiki/JsonModules: 1036 | -- 1037 | -- * When encoding lua for JSON, Sparse numeric arrays are now handled by 1038 | -- spitting out full arrays, such that 1039 | -- JSON:encode({"one", "two", [10] = "ten"}) 1040 | -- returns 1041 | -- ["one","two",null,null,null,null,null,null,null,"ten"] 1042 | -- 1043 | -- In 20100810.2 and earlier, only up to the first non-null value would have been retained. 1044 | -- 1045 | -- * When encoding lua for JSON, numeric value NaN gets spit out as null, and infinity as "1+e9999". 1046 | -- Version 20100810.2 and earlier created invalid JSON in both cases. 1047 | -- 1048 | -- * Unicode surrogate pairs are now detected when decoding JSON. 1049 | -- 1050 | -- 20100810.2 added some checking to ensure that an invalid Unicode character couldn't leak in to the UTF-8 encoding 1051 | -- 1052 | -- 20100731.1 initial public release 1053 | -- 1054 | -------------------------------------------------------------------------------- /bot.lua: -------------------------------------------------------------------------------- 1 | tdcli = dofile('./tdcli.lua') 2 | URL = require "socket.url" 3 | http = require "socket.http" 4 | https = require "ssl.https" 5 | ltn12 = require "ltn12" 6 | serpent = require ("serpent") 7 | db = require('redis') 8 | redis = db.connect('127.0.0.1', 6379) 9 | JSON = require('dkjson') 10 | http.TIMEOUT = 10 11 | --###############################-- 12 | sudo_users = {305941305} 13 | 14 | --##########GetMessage###########-- 15 | local function getMessage(chat_id, message_id,callback) 16 | tdcli_function ({ 17 | ID = "GetMessage", 18 | chat_id_ = chat_id, 19 | message_id_ = message_id 20 | }, callback, nil) 21 | end 22 | 23 | --###############################-- 24 | function is_sudo(msg) 25 | local var = false 26 | for v,user in pairs(sudo_users) do 27 | if user == msg.sender_user_id_ then 28 | var = true 29 | end 30 | end 31 | return var 32 | end 33 | --###############################-- 34 | function is_owner(msg) 35 | local var = false 36 | local group_mods = redis:get('owners:'..msg.chat_id_) 37 | if group_mods == tostring(msg.sender_user_id_) then 38 | var = true 39 | end 40 | for v, user in pairs(sudo_users) do 41 | if user == msg.sender_user_id_ then 42 | var = true 43 | end 44 | end 45 | return var 46 | end 47 | --###############################-- 48 | function is_momod(msg) 49 | local var = false 50 | if redis:sismember('mods:'..msg.chat_id_,msg.sender_user_id_) then 51 | var = true 52 | end 53 | if redis:get('owners:'..msg.chat_id_) == tostring(msg.sender_user_id_) then 54 | var = true 55 | end 56 | for v, user in pairs(sudo_users) do 57 | if user == msg.sender_user_id_ then 58 | var = true 59 | end 60 | end 61 | return var 62 | end 63 | --###############################-- 64 | function is_banned(msg) 65 | local var = false 66 | local chat_id = msg.chat_id_ 67 | local user_id = msg.sender_user_id_ 68 | local hash = 'banned:'..chat_id 69 | local banned = redis:sismember(hash, user_id) 70 | if banned then 71 | var = true 72 | end 73 | return var 74 | end 75 | --###############################-- 76 | function is_muted(msg) 77 | local var = false 78 | local chat_id = msg.chat_id_ 79 | local user_id = msg.sender_user_id_ 80 | local hash = 'muteusers:'..chat_id 81 | local muted = redis:sismember(hash, user_id) 82 | if muted then 83 | var = true 84 | end 85 | return var 86 | end 87 | --###############################-- 88 | function is_gbanned(msg) 89 | local var = false 90 | local chat_id = msg.chat_id_ 91 | local user_id = msg.sender_user_id_ 92 | local hash = 'gbanned:' 93 | local banned = redis:sismember(hash, user_id) 94 | if banned then 95 | var = true 96 | end 97 | return var 98 | end 99 | --###############################-- 100 | function vardump(value) 101 | print(serpent.block(value, {comment=false})) 102 | end 103 | function run(data,edited_msg) 104 | local msg = data.message_ 105 | if edited_msg then 106 | msg = data 107 | end 108 | -- vardump(msg) 109 | local input = msg.content_.text_ 110 | local chat_id = msg.chat_id_ 111 | local user_id = msg.sender_user_id_ 112 | local botgp = redis:get("addedgp"..chat_id) 113 | local wlcmsg = "wlc"..chat_id 114 | local setwlc = "setwlc"..chat_id 115 | local floodMax = "floodMax"..chat_id 116 | local floodTime = "floodTime"..chat_id 117 | local mutehash = 'muteall:'..chat_id 118 | local hashflood = "flood"..chat_id 119 | local hashbot = "bot"..chat_id 120 | local hashlink = "link"..chat_id 121 | local hashtag = "tag"..chat_id 122 | local hashusername = "username"..chat_id 123 | local hashforward = "forward"..chat_id 124 | local hasharabic = "arabic"..chat_id 125 | local hashtgservice = "tgservice"..chat_id 126 | local hasheng = "eng"..chat_id 127 | local hashbadword = "badword"..chat_id 128 | local hashedit = "edit"..chat_id 129 | local hashinline = "inline"..chat_id 130 | local hashemoji = "emoji"..chat_id 131 | local hashall = "all"..chat_id 132 | local hashsticker = "sticker"..chat_id 133 | local hashgif = "gif"..chat_id 134 | local hashcontact = "contact"..chat_id 135 | local hashphoto = "photo"..chat_id 136 | local hashaudio = "audio"..chat_id 137 | local hashvoice = "voice"..chat_id 138 | local hashvideo = "video"..chat_id 139 | local hashdocument = "document"..chat_id 140 | local hashtext1 = "text"..chat_id 141 | if not botgp and not is_sudo(msg) then 142 | return false 143 | end 144 | if msg.chat_id_ then 145 | local id = tostring(msg.chat_id_) 146 | if id:match('-100(%d+)') then 147 | chat_type = 'super' 148 | elseif id:match('^(%d+)') then 149 | chat_type = 'user' 150 | else 151 | chat_type = 'group' 152 | end 153 | end 154 | ------------------------------------------------------------------------------------------- 155 | if redis:get(mutehash) == 'Enable' and not is_momod(msg) then 156 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 157 | end 158 | if msg.content_.sticker_ and redis:get(hashsticker) == 'Enable' and not is_momod(msg) then 159 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 160 | end 161 | if msg.content_.animation_ and redis:get(hashgif) == 'Enable' and not is_momod(msg) then 162 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 163 | end 164 | if msg.content_.contact_ and redis:get(hashgif) == 'Enable' and not is_momod(msg) then 165 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 166 | end 167 | if msg.content_.photo_ and redis:get(hashgif) == 'Enable' and not is_momod(msg) then 168 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 169 | end 170 | if msg.content_.audio_ and redis:get(hashaudio) == 'Enable' and not is_momod(msg) then 171 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 172 | end 173 | if msg.content_.voice_ and redis:get(hashvoice) == 'Enable' and not is_momod(msg) then 174 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 175 | end 176 | if msg.content_.video_ and redis:get(hashvideo) == 'Enable' and not is_momod(msg) then 177 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 178 | end 179 | if msg.content_.document_ and redis:get(hashdocument) == 'Enable' and not is_momod(msg) then 180 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 181 | end 182 | if msg.forward_info_ and redis:get(hashforward) == 'Enable' and not is_momod(msg) then 183 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 184 | end 185 | if msg.via_bot_user_id_ ~= 0 and redis:get(hashinline) == 'Enable' and not is_momod(msg) then 186 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 187 | end 188 | 189 | local floodMax = redis:get('floodMax') or 10 190 | local floodTime = redis:get('floodTime') or 150 191 | if msg and redis:get(hashflood) == 'Enable' and not is_momod(msg) then 192 | local hash = 'flood:'..msg.sender_user_id_..':'..msg.chat_id_..':msg-num' 193 | local msgs = tonumber(redis:get(hash) or 0) 194 | if msgs > (floodMax - 1) then 195 | tdcli.changeChatMemberStatus(msg.chat_id_, msg.sender_user_id_, "Kicked") 196 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر _'..msg.sender_user_id_..' به دلیل ارسال اسپم حذف شد!', 1, 'md') 197 | redis:setex(hash, floodTime, msgs+1) 198 | end 199 | end 200 | 201 | 202 | if msg.content_.ID == "MessageText" then 203 | local is_link_msg = input:match("[Tt][Ee][Ll][Ee][Gg][Rr][Aa][Mm].[Mm][Ee]/") or input:match("[Tt].[Mm][Ee]/") 204 | if redis:get(hashlink) and is_link_msg and not is_momod(msg) then 205 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 206 | end 207 | if redis:get(hashtag) and input:match("#") and not is_momod(msg) then 208 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 209 | end 210 | if redis:get(hashusername) and input:match("@") and not is_momod(msg) then 211 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 212 | 213 | end 214 | if redis:get(hasharabic) and input:match("[\216-\219][\128-\191]") and not is_momod(msg) then 215 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 216 | end 217 | local is_english_msg = input:match("[a-z]") or input:match("[A-Z]") 218 | if redis:get(hasheng) and msg.content_.text_ and is_english_msg and not is_momod(msg) then 219 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 220 | end 221 | local is_fosh_msg = input:match("کیر") or input:match("کس") or input:match("کون") or input:match("85") or input:match("جنده") or input:match("ننه") or input:match("ننت") or input:match("مادر") or input:match("قهبه") or input:match("گایی") or input:match("سکس") or input:match("kir") or input:match("kos") or input:match("kon") or input:match("nne") or input:match("nnt") 222 | if redis:get(hashbadword) and is_fosh_msg and not is_momod(msg) then 223 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 224 | end 225 | local is_emoji_msg = input:match("😀") or input:match("😬") or input:match("😁") or input:match("😂") or input:match("😃") or input:match("😄") or input:match("😅") or input:match("☺️") or input:match("🙃") or input:match("🙂") or input:match("😊") or input:match("😉") or input:match("😇") or input:match("😆") or input:match("😋") or input:match("😌") or input:match("😍") or input:match("😘") or input:match("😗") or input:match("😙") or input:match("😚") or input:match("🤗") or input:match("😎") or input:match("🤓") or input:match("🤑") or input:match("😛") or input:match("😏") or input:match("😶") or input:match("😐") or input:match("😑") or input:match("😒") or input:match("🙄") or input:match("🤔") or input:match("😕") or input:match("😔") or input:match("😡") or input:match("😠") or input:match("😟") or input:match("😞") or input:match("😳") or input:match("🙁") or input:match("☹️") or input:match("😣") or input:match("😖") or input:match("😫") or input:match("😩") or input:match("😤") or input:match("😲") or input:match("😵") or input:match("😭") or input:match("😓") or input:match("😪") or input:match("😥") or input:match("😢") or input:match("🤐") or input:match("😷") or input:match("🤒") or input:match("🤕") or input:match("😴") or input:match("💋") or input:match("❤️") 226 | if redis:get(hashemoji) and is_emoji_msg and not is_momod(msg) then 227 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 228 | end 229 | end 230 | --###############################-- 231 | local text = msg.content_.text_ 232 | -- if text and text:match('[QWERTYUIOPASDFGHJKLZXCVBNM]') then 233 | -- text = text:lower() 234 | -- end 235 | if msg.content_.ID == "MessageText" then 236 | msg_type = 'text' 237 | if msg_type == 'text' and text and text:match('^[/$#!]') then 238 | text = text:gsub('^[/$!#]','') 239 | end 240 | end 241 | if text == "p r" and is_sudo(msg) then 242 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'ربات* موفقیت ریلود شد*',1, 'html') 243 | io.popen("pkill tg") 244 | end 245 | if text == "ping" then 246 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, '*انلاینم....!*',1, 'md') 247 | end 248 | local hashadd = "addedgp"..chat_id 249 | if text == "add" and is_sudo(msg) then 250 | if botgp then 251 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, '*ربات از قبل با موفقیت فعال شده بود!*', 1, 'md') 252 | else 253 | redis:set(hashadd, true) 254 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, '*ربات با موفقیت فعال شد.*', 1, 'md') 255 | end 256 | end 257 | if text == "rem" and is_sudo(msg) then 258 | if not botgp then 259 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, '*ربات از قبل با موفقیت غیر فعال شده بود!*', 1, 'md') 260 | else 261 | redis:del(hashadd, true) 262 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, '*ربات با موفقیت غیر فعال شد.*', 1, 'md') 263 | end 264 | end 265 | 266 | if text == "setowner" and is_owner(msg) and msg.reply_to_message_id_ then 267 | function setowner_reply(extra, result, success) 268 | redis:del('owners:'..result.chat_id_) 269 | redis:set('owners:'..result.chat_id_,result.sender_user_id_) 270 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر *'..result.sender_user_id_..'* باموفقیت مالک گروه شد', 1, 'md') 271 | end 272 | getMessage(chat_id,msg.reply_to_message_id_,setowner_reply,nil) 273 | end 274 | if text == "owners" then 275 | local hash = 'owners:'..chat_id 276 | local owner = redis:get(hash) 277 | if owner == nil then 278 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'گروه هیچ مالکی ندارد ', 1, 'md') 279 | end 280 | local owner_list = redis:get('owners:'..chat_id) 281 | text = '* مالک گروه:* '..owner_list 282 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 283 | end 284 | if text and text:match('^setowner (.*)') and not text:find('@') and is_sudo(msg) then 285 | local so = {string.match(text, "^setowner (.*)$")} 286 | redis:del('owners:'..chat_id) 287 | redis:set('owners:'..chat_id,so[1]) 288 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..so[1]..' به موفقیت مالک گروه شد', 1, 'md') 289 | end 290 | if text and text:match('^setowner (.*)') and text:find('@') and is_owner(msg) then 291 | local sou = {string.match(text, "^setowner (.*)$")} 292 | function Inline_Callback_(arg, data) 293 | redis:del('owners:'..chat_id) 294 | redis:set('owners:'..chat_id,sou[1]) 295 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..sou[1]..' با موفقیت مالک گروه شد', 1, 'md') 296 | end 297 | tdcli_function ({ID = "SearchPublicChat",username_ =sou[1]}, Inline_Callback_, nil) 298 | end 299 | 300 | 301 | if text == "promote" and is_sudo(msg) and msg.reply_to_message_id_ then 302 | function setmod_reply(extra, result, success) 303 | redis:sadd('mods:'..result.chat_id_,result.sender_user_id_) 304 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..result.sender_user_id_..' به لیست ناظران گروه اضافه شد', 1, 'md') 305 | end 306 | getMessage(chat_id,msg.reply_to_message_id_,setmod_reply,nil) 307 | end 308 | if text == "demote" and is_sudo(msg) and msg.reply_to_message_id_ then 309 | function remmod_reply(extra, result, success) 310 | redis:srem('mods:'..result.chat_id_,result.sender_user_id_) 311 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..result.sender_user_id_..' از لیست ناظران گروه حذف شد', 1, 'md') 312 | end 313 | getMessage(chat_id,msg.reply_to_message_id_,remmod_reply,nil) 314 | end 315 | if text and text:match('^promote (.*)') and not text:find('@') and is_sudo(msg) then 316 | local pm = {string.match(text, "^promote (.*)$")} 317 | redis:sadd('mods:'..chat_id,pm[1]) 318 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..pm[1]..' به لیست ناظران گروه اضافه شد', 1, 'md') 319 | end 320 | if text and text:match('^demote (.*)') and not text:find('@') and is_sudo(msg) then 321 | local dm = {string.match(text, "^demote (.*)$")} 322 | redis:srem('mods:'..chat_id,dm[1]) 323 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..dm[1]..' از لیست ناظران گروه حذف شد', 1, 'md') 324 | end 325 | if text == "modlist" then 326 | if redis:scard('mods:'..chat_id) == 0 then 327 | text = "لیست ناظران گروه خالی است" 328 | else 329 | text = "لیست ناظران گروه\n" 330 | for k,v in pairs(redis:smembers('mods:'..chat_id)) do 331 | text = text..""..k.." - "..v.."\n" 332 | end 333 | end 334 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'html') 335 | end 336 | --[[if txet == "ban" and is_momod(msg) then 337 | function ban_reply(extra, result, success) 338 | redis:sadd('banned:'..result.chat_id_,result.sender_user_id_) 339 | tdcli.changeChatMemberStatus(result.chat_id_, result.sender_user_id_, 'Kicked') 340 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..result.sender_user_id_..' به لیست افراد بن شده اضافه شد', 1, 'md') 341 | end 342 | getMessage(chat_id,reply,ban_reply,nil) 343 | end 344 | end 345 | if text and text:match('^ban (.*)') and not text:find('@') and is_momod(msg) then 346 | local ki = {string.match(text, "^ban (.*)$")} 347 | redis:sadd('banned:'..chat_id,ki[1]) 348 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..ki[1]..' به لیست افراد بن شده اضافه شد', 1, 'md') 349 | tdcli.changeChatMemberStatus(chat_id, ki[1], 'Kicked') 350 | end 351 | if text and text:match('^ban @(.*)') and is_momod(msg) then 352 | local ku = {string.match(text, "^ban @(.*)$")} 353 | redis:sadd('banned:'..chat_id,ku[1]) 354 | function Inline_Callback_(arg, data) 355 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..ku[1]..' به لیست افراد بن شده اضافه شد', 1, 'html') 356 | tdcli.changeChatMemberStatus(chat_id, data.id_, 'Kicked') 357 | end 358 | 359 | if txet == "unban" and is_momod(msg) then 360 | function unban_reply(extra, result, success) 361 | redis:srem('banned:'..result.chat_id_,result.sender_user_id_) 362 | 363 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..result.sender_user_id_..' از لیست افراد بن شده حذف شد', 1, 'md') 364 | end 365 | getMessage(chat_id,reply,unban_reply,nil) 366 | end 367 | if text and text:match('^unban (.*)') and not text:find('@') and is_momod(msg) then 368 | local ki = {string.match(text, "^unban (.*)$")} 369 | redis:srem('banned:'..chat_id,ub[1]) 370 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..ub[1]..' از لیست افراد بن شده حذف شد', 1, 'md') 371 | end 372 | if text and text:match('^unban @(.*)') and is_momod(msg) then 373 | local ku = {string.match(text, "^unban @(.*)$")} 374 | redis:srem('banned:'..chat_id,unb[1]) 375 | function Inline_Callback_(arg, data) 376 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..unb[1]..' از لیست افراد بن شده حذف شد', 1, 'html') 377 | end 378 | tdcli_function ({ID = "SearchPublicChat",username_ =unb[1]}, Inline_Callback_, nil) 379 | end]]-- 380 | if text == "banlist" then 381 | 382 | if redis:scard('banned:'..chat_id) == 0 then 383 | text = "لیست بن های گروه خالی است" 384 | else 385 | text = "بن لیست گروه\n" 386 | for k,v in pairs(redis:smembers('banned:'..chat_id)) do 387 | text = text..""..k.." - "..v.."\n" 388 | end 389 | end 390 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'html') 391 | end 392 | if text == "muteuser" and is_momod(msg) and msg.reply_to_message_id_ then 393 | function setmute_reply(extra, result, success) 394 | redis:sadd('muteusers:'..result.chat_id_,result.sender_user_id_) 395 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..result.sender_user_id_..' به لیست ساکت ها اضافه شد', 1, 'md') 396 | end 397 | getMessage(chat_id,msg.reply_to_message_id_,setmute_reply,nil) 398 | end 399 | if text == "unmuteuser" and is_momod(msg) and msg.reply_to_message_id_ then 400 | function demute_reply(extra, result, success) 401 | redis:srem('muteusers:'..result.chat_id_,result.sender_user_id_) 402 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..result.sender_user_id_..' از لیست ساکت ها حذف شد', 1, 'md') 403 | end 404 | getMessage(chat_id,msg.reply_to_message_id_,demute_reply,nil) 405 | end 406 | if text and text:match("^muteuser (%d+)") and is_momod(msg) then 407 | local mt = {string.match(text, "^muteuser (%d+)$")} 408 | redis:sadd('muteusers:'..chat_id,mt[1]) 409 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..mt[1]..' به لیست ساکت ها اضافه شد', 1, 'md') 410 | end 411 | if text and text:match('^unmuteuser (%d+)$') and is_momod(msg) then 412 | local umt = {string.match(text, "^muteuser (%d+)$")} 413 | redis:srem('muteusers:'..chat_id,umt[1]) 414 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'کاربر '..umt[1]..' از لیست ساکت ها حذف شد', 1, 'md') 415 | end 416 | if text == "mutelist" then 417 | if redis:scard('muteusers:'..chat_id) == 0 then 418 | text = "لیست بن های گروه خالی است" 419 | else 420 | text = "بن لیست گروه\n" 421 | for k,v in pairs(redis:smembers('muteusers:'..chat_id)) do 422 | text = text..""..k.." - "..v.."\n" 423 | end 424 | end 425 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'html') 426 | end 427 | if text and text:match("^clean (.*)$") and is_momod(msg) then 428 | local txt = {string.match(text, "^(clean) (.*)$")} 429 | if txt[2] == 'banlist' then 430 | redis:del('banned:'..msg.chat_id_) 431 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, '_لیست بن با موفقیت حذف شد_', 1, 'md') 432 | end 433 | if txt[2] == 'modlist' then 434 | redis:del('mods:'..msg.chat_id_) 435 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, '_لیست ناظران گروه با موفقیت حذف شد _', 1, 'md') 436 | end 437 | if txt[2] == 'mutelist' then 438 | redis:del('muted:'..msg.chat_id_) 439 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, '_لیست ساکت های گروه با موفقیت حذف شد_', 1, 'md') 440 | end 441 | 442 | end 443 | if text == "delall" and msg.reply_to_message_id_ then 444 | function delall(extra, result, success) 445 | tdcli.deleteMessagesFromUser(result.chat_id_, result.sender_user_id_) 446 | end 447 | getMessage(chat_id, msg.reply_to_message_id_, delall, nil) 448 | end 449 | if text and text:match('^setlink (.*)/joinchat/(.*)') and is_owner(msg) then 450 | local l = {string.match(text, '^setlink (.*)/joinchat/(.*)')} 451 | redis:set('gplink'..chat_id,"https://t.me/joinchat/"..l[2]) 452 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'لینک گروه با موفقیت ثبت شد.', 1, 'html') 453 | end 454 | if text == "link" and is_momod(msg) then 455 | local linkgp = redis:get('gplink'..chat_id) 456 | if not linkgp then 457 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'لینک ورود به گروه تنظیم نشده.\nثبت لینک جدید با دستور\n/setlink لینک', 1, 'html') 458 | return 459 | else 460 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'لینک گروه :\n'..linkgp, 1, 'html') 461 | end 462 | end 463 | if text and text:match('^setrules (.*)') and is_owner(msg) then redis:set('gprules'..chat_id,text:match('^setrules (.*)')) 464 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, '*قوانین گروه با موفقیت تنظیم شد*', 1, 'md') 465 | end 466 | if text == "rules" then 467 | rules = redis:get('gprules'..chat_id) 468 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, 'قوانین گروه :\n'..rules, 1, 'html') 469 | end 470 | if text and text:match('^setname (.*)$') and is_momod(msg) then 471 | local matches = {string.match(text, '^setname (.*)$')} 472 | tdcli.changeChatTitle(chat_id, matches[1]) 473 | end 474 | if text == "leave" and is_sudo(msg) then 475 | function botid(a,b,c) 476 | tdcli.changeChatMemberStatus(chat_id, b.id_, "Left") 477 | end 478 | tdcli.getMe(botid, nil) 479 | end 480 | if text == "settings" and is_momod(msg) then 481 | local text = "————————\n_📛تنظیمات گروه📛_ \n————————\n◾️قفل‌لینک : "..(redis:get(hashlink) or "Disabled").."\n◾️️قفل‌فلود : "..(redis:get(hashflood) or "Disabled").."\n◾️قفل‌فوروارد : "..(redis:get(hashforward) or "Disabled").."\n◾️قفل‌تگ(#) "..(redis:get(hashtag) or "Disabled").."\n◾️قفل‌یوزرنیم(@) : "..(redis:get(hashusername) or "Disabled").."\n◾️قفل‌ربات : "..(redis:get(hashbot) or "Disabled").."\n◾️قفل‌ورود‌و‌خروج "..(redis:get(hashtgservice) or "Disabled").."\n◾️قفل‌عربی/فارسی : "..(redis:get(hasharabic) or "Disabled").."\n◾️قفل‌انگلیسی : "..(redis:get(hasheng) or "Disabled").."\n" 482 | text = text.."◾️️قفل‌فحش : "..(redis:get(hashbadword) or "Disabled").."\n◾️قفل‌مخاطب : "..(redis:get(hashcontact) or "Disabled").."\n◾️قفل‌استیکر : "..(redis:get(hashsticker) or "Disabled").."\n" 483 | text = text.."◾️قفل‌کیبور‌انلاین : "..(redis:get(hashinline) or "Disabled").."\n◾️قفل‌ایموجی : "..(redis:get(hashemoji) or "Disabled").."\n" 484 | text = text.."————————\n_📛فیلتر لیست📛_\n————————\n◾️فیلتر‌گیف : "..(redis:get(hashgif) or "Disabled").."\n◾️فیلتر‌عکس : "..(redis:get(hashphoto) or "Disabled").."\n◾️فیلتر‌اهنگ : "..(redis:get(hashaudio) or "Disabled").."\n" 485 | text = text.."◾️فیلتر‌‌‌وییس : "..(redis:get(hashvoice) or "Disabled").."\n◾️فیلتر‌ویدیو : "..(redis:get(hashvideo) or "Disabled").."\n◾️فیلتر‌فایل : "..(redis:get(hashdocument) or "Disabled").."\n◾️فیلتر‌متن : "..(redis:get(hashtext1) or "Disabled").."\n" 486 | text = text.."————————\n_📛دیگر تنظیمات📛_\n————————\n◾️زمان فلود : *"..floodTime.."*\n◾️تعداد فلود : *"..floodMax.."*" 487 | text = string.gsub(text, "Enable", "✅") 488 | text = string.gsub(text, "Disabled", "⛔️") 489 | text = string.gsub(text, ":", "*>*") 490 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 491 | end 492 | if text == "lock flood" and is_momod(msg) then 493 | if redis:get(hashflood) == "Enable" then 494 | local text = "قفل فلود از قبل فعال شده بود!" 495 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 496 | else 497 | redis:set(hashflood ,"Enable") 498 | local text = "قفل فلود باموفقیت فعال شد!" 499 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 500 | end 501 | end 502 | if text == "unlock flood" and is_momod(msg) then 503 | if not redis:get(hashflood) == "Enable" then 504 | local text = "قفل فلود از قبل غیر فعال شده بود!" 505 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 506 | else 507 | redis:del(hashflood) 508 | local text = "قفل فلود باموفقیت غیر فعال شد!" 509 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 510 | end 511 | end 512 | 513 | if text == "lock bots" and is_momod(msg) then 514 | if redis:get(hashbot) == "Enable" then 515 | local text = "قفل ربات از قبل فعال شده بود!" 516 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 517 | else 518 | redis:set(hashbot ,"Enable") 519 | local text = "قفل ورود ربات باموفقیت فعال شد!" 520 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 521 | end 522 | end 523 | if text == "unlock bots" and is_momod(msg) then 524 | if not redis:get(hashbot) == "Enable" then 525 | local text = "قفل ربات از قبل غیر فعال شده بود!" 526 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 527 | else 528 | redis:del(hashbot) 529 | local text = "قفل ورود ربات باموفقیت غیر فعال شد!" 530 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 531 | end 532 | end 533 | 534 | if text == "lock tgservice" and is_momod(msg) then 535 | if redis:get(hashtgservice) == "Enable" then 536 | local text = "قفل پیام ورود و خروج از قبل فعال شده بود!" 537 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 538 | else 539 | redis:set(hashtgservice ,"Enable") 540 | local text = "قفل پیام ورود و خروج باموفقیت فعال شد" 541 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 542 | end 543 | end 544 | if text == "unlock tgservice" and is_momod(msg) then 545 | if not redis:get(hashtgservice) == "Enable" then 546 | local text = "قفل پیام ورود و خروج از قبل غیر فعال شده بود!" 547 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 548 | else 549 | redis:del(hashtgservice) 550 | local text = "قفل پیام ورود و خروج باموفقیت غیرفعال شد" 551 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 552 | end 553 | end 554 | 555 | if text == "lock links" and is_momod(msg) then 556 | if redis:get(hashlink) == "Enable" then 557 | local text = "قفل لینک از قبل فعال شده بود!" 558 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 559 | else 560 | redis:set(hashlink ,"Enable") 561 | local text = "قفل لینک باموفقیت فعال شد!" 562 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 563 | end 564 | end 565 | if text == "unlock links" and is_momod(msg) then 566 | if not redis:get(hashlink) == "Enable" then 567 | local text = "قفل لینک از قبل غیر فعال شده بود!" 568 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 569 | else 570 | redis:del(hashlink) 571 | local text = "قفل لینک باموفقیت غیر فعال شد!" 572 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 573 | end 574 | end 575 | 576 | if text == "lock tag" and is_momod(msg) then 577 | if redis:get(hashtag) == "Enable" then 578 | local text = "قفل تگ [#] از قبل فعال شده بود!" 579 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 580 | else 581 | redis:set(hashtag ,"Enable") 582 | local text = "قفل تگ [#] باموفقیت فعال شد!" 583 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 584 | end 585 | end 586 | if text == "unlock tag" and is_momod(msg) then 587 | if not redis:get(hashtag) == "Enable" then 588 | local text = "قفل تگ [#] از قبل غیر فعال شده بود!" 589 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 590 | else 591 | redis:del(hashtag) 592 | local text = "قفل تگ [#] باموفقیت غیر فعال شد!" 593 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 594 | end 595 | end 596 | 597 | if text == "lock username" and is_momod(msg) then 598 | if redis:get(hashusername) == "Enable" then 599 | local text = "قفل یوزرنیم (@) از قبل فعال شده بود!" 600 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 601 | else 602 | redis:set(hashusername ,"Enable") 603 | local text = "قفل یوزرنیم (@) باموفقیت فعال شد!" 604 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 605 | end 606 | end 607 | if text == "unlock username" and is_momod(msg) then 608 | if not redis:get(hashusername) == "Enable" then 609 | local text = "قفل یوزرنیم (@) از قبل غیر فعال شده بود!" 610 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 611 | else 612 | redis:del(hashusername) 613 | local text = "قفل نام یوزرنیم (@) باموفقیت غیر فعال شد!" 614 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 615 | end 616 | end 617 | 618 | if text == "lock forward" and is_momod(msg) then 619 | if redis:get(hashforward) == "Enable" then 620 | local text = "قفل فوروارد از قبل فعال شده بود!" 621 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 622 | else 623 | redis:set(hashforward ,"Enable") 624 | local text = "قفل فروارد باموفقیت فعال شد!" 625 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 626 | end 627 | end 628 | if text == "unlock forward" and is_momod(msg) then 629 | if not redis:get(hashforward) == "Enable" then 630 | local text = "قفل فوروارد از قبل غیر فعال شده بود!" 631 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 632 | else 633 | redis:del(hashforward) 634 | local text = "قفل فروارد باموفقیت غیر فعال شد!" 635 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 636 | end 637 | end 638 | 639 | if text == "lock arabic" and is_momod(msg) then 640 | if redis:get(hasharabic) == "Enable" then 641 | local text = "قفل عربی/فارسی از قبل فعال شده بود!" 642 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 643 | else 644 | redis:set(hasharabic ,"Enable") 645 | local text = "قفل زبان عربی/فارسی باموفقیت فعال شد!" 646 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 647 | end 648 | end 649 | if text == "unlock arabic" and is_momod(msg) then 650 | if not redis:get(hasharabic) == "Enable" then 651 | local text = "قفل عربی/فارسی از قبل غیر فعال شده بود!" 652 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 653 | else 654 | redis:del(hasharabic) 655 | local text = "قفل زبات عربی/فارسی باموفقیت غیر فعال شد!" 656 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 657 | end 658 | end 659 | 660 | if text == "lock english" and is_momod(msg) then 661 | if redis:get(hasheng) == "Enable" then 662 | local text = "قفل زبان انگلیسی از قبل فعال شده بود!" 663 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 664 | else 665 | redis:set(hasheng ,"Enable") 666 | local text = "قفل زبان انگلیسی باموفقیت فعال شد!" 667 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 668 | end 669 | end 670 | if text == "unlock english" and is_momod(msg) then 671 | if not redis:get(hasheng) == "Enable" then 672 | local text = "قفل زبان انگلیسی از قبل غیر فعال شده بود!" 673 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 674 | else 675 | redis:del(hasheng) 676 | local text = "قفل زبان انگلیسی باموفقیت غیر فعال شد!" 677 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 678 | end 679 | end 680 | if text == "lock fosh" and is_momod(msg) then 681 | if redis:get(hashbadword) == "Enable" then 682 | local text = "قفل فحش از قبل فعال شده بود!" 683 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 684 | else 685 | redis:set(hashbadword ,"Enable") 686 | local text = "قفل فحش باموفقیت فعال شد!" 687 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 688 | end 689 | end 690 | if text == "unlock fosh" and is_momod(msg) then 691 | if not redis:get(hashbadword) == "Enable" then 692 | local text = "قفل فحش از قبل غیر فعال شده بود!" 693 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 694 | else 695 | redis:del(hashbadword) 696 | local text = "قفل فحش باموفقیت غیر فعال شد!" 697 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 698 | end 699 | end 700 | 701 | if text == "lock inline" and is_momod(msg) then 702 | if redis:get(hashinline) == "Enable" then 703 | local text = "قفل دکمه شیشه ای از قبل فعال شده بود!" 704 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 705 | else 706 | redis:set(hashinline ,"Enable") 707 | local text = "قفل دکمه شیشه ای باموفقیت فعال شد!" 708 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 709 | end 710 | end 711 | if text == "unlock inline" and is_momod(msg) then 712 | if not redis:get(hashinline) == "Enable" then 713 | local text = "قفل دکمه شیشه ای از قبل غیر فعال شده بود!" 714 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 715 | else 716 | redis:del(hashinline) 717 | local text = "قفل دکمه شیشه ای باموفقیت غیر فعال شد!" 718 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 719 | end 720 | end 721 | 722 | if text == "lock emoji" and is_momod(msg) then 723 | if redis:get(hashemoji) == "Enable" then 724 | local text = "قفل ایموجی (😄) از قبل فعال شده بود!" 725 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 726 | else 727 | redis:set(hashemoji ,"Enable") 728 | local text = "قفل ایموجی (😄) باموفقیت فعال شد!" 729 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 730 | end 731 | end 732 | 733 | if text == "unlock emoji" and is_momod(msg) then 734 | if not redis:get(hashemoji) == "Enable" then 735 | local text = "قفل ایموجی (😄) از قبل غیر فعال شده بود!" 736 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 737 | else 738 | redis:del(hashemoji) 739 | local text = "قفل ایموجی (😄) باموفقیت غیر فعال شد!" 740 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 741 | end 742 | end 743 | 744 | if text == "lock contact" and is_momod(msg) then 745 | if redis:get(hashcontact) == "Enable" then 746 | local text = "قفل اشتراک گذاری مخاطبین از قبل فعال شده بود!" 747 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 748 | else 749 | redis:set(hashcontact ,"Enable") 750 | local text = "قفل اشتراک گذاری مخاطبین باموفقیت فعال شد!" 751 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 752 | end 753 | end 754 | if text == "unlock contact" and is_momod(msg) then 755 | if not redis:get(hashcontact) == "Enable" then 756 | local text = "قفل اشتراک گذاری مخاطبین از قبل غیر فعال شده بود!" 757 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 758 | else 759 | redis:del(hashcontact) 760 | local text = "قفل اشتراک گذاری مخاطبین باموفقیت غیر فعال شد!" 761 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 762 | end 763 | end 764 | 765 | if text == "lock sticker" and is_momod(msg) then 766 | if redis:get(hashcontact) == "Enable" then 767 | local text = "قفل ارسال استیکر از قبل فعال شده بود!" 768 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 769 | else 770 | redis:set(hashsticker ,"Enable") 771 | local text = "قفل ارسال استیکر باموفقیت فعال شد!" 772 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 773 | end 774 | end 775 | if text == "unlock sticker" and is_momod(msg) then 776 | if not redis:get(hashcontact) == "Enable" then 777 | local text = "قفل ارسال استیکر از قبل غیر فعال شده بود!" 778 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 779 | else 780 | redis:del(hashsticker) 781 | local text = "قفل ارسال استیکر باموفقیت غیر فعال شد!" 782 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 783 | end 784 | end 785 | 786 | if text == "mute gif" and is_momod(msg) then 787 | redis:set(hashgif ,"Enable") 788 | local text = "فیتلر گیف (عکس متحرک) باموفقیت فعال شد!" 789 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 790 | end 791 | if text == "unmute gif" and is_momod(msg) then 792 | redis:del(hashgif) 793 | local text = "فیتلر گیف (عکس متحرک) غیر فعال شد!" 794 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 795 | end 796 | if text == "mute photo" and is_momod(msg) then 797 | redis:set(hashphoto ,"Enable") 798 | local text = "فیتلرعکس باموفقیت فعال شد!" 799 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 800 | end 801 | if text == "unmute photo" and is_momod(msg) then 802 | redis:del(hashphoto) 803 | local text = "فیتلرعکس باموفقیت غیر فعال شد!" 804 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 805 | end 806 | if text == "mute audio" and is_momod(msg) then 807 | redis:set(hashaudio ,"Enable") 808 | local text = "فیتلر اهنگ باموفقیت فعال شد!" 809 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 810 | end 811 | if text == "unmute audio" and is_momod(msg) then 812 | redis:del(hashaudio) 813 | local text = "فیتلر اهنگ باموفقیت غیر فعال شد!" 814 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 815 | end 816 | if text == "mute voice" and is_momod(msg) then 817 | redis:set(hashvoice ,"Enable") 818 | local text = "فیتلر صدا باموفقیت فعال شد!" 819 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 820 | end 821 | if text == "unmute voice" and is_momod(msg) then 822 | redis:del(hashvoice) 823 | local text = "فیتلر صدا باموفقیت غیر فعال شد!" 824 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 825 | end 826 | if text == "mute video" and is_momod(msg) then 827 | redis:set(hashvideo ,"Enable") 828 | local text = "فیتلر فیلم باموفقیت فعال شد!" 829 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 830 | end 831 | if text == "unmute video" and is_momod(msg) then 832 | redis:del(hashvideo) 833 | local text = "فیتلر فیلم باموفقیت غیر فعال شد!" 834 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 835 | end 836 | if text == "mute document" and is_momod(msg) then 837 | redis:set(hashdocument ,"Enable") 838 | local text = "فیتلر فایل باموفقیت فعال شد!" 839 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 840 | end 841 | if text == "unmute document" and is_momod(msg) then 842 | 843 | redis:del(hashdocument) 844 | local text = "فیتلر فایل باموفقیت غیر فعال شد!" 845 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 846 | end 847 | if text == "mute text" and is_momod(msg) then 848 | redis:set(hashtext1 ,"Enable") 849 | local text = "فیتلر متن با موفقیت فعال شد!" 850 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 851 | end 852 | if text == "unmute text" and is_momod(msg) then 853 | redis:del(hashtext1) 854 | local text = "فیتلر متن با موفقیت غیر فعال شد!" 855 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, text, 1, 'md') 856 | end 857 | if text == 'pin' and is_momod(msg) then 858 | tdcli.pinChannelMessage(msg.chat_id_, msg.reply_to_message_id_, 1) 859 | end 860 | if text == "unpin" and is_momod(msg) then 861 | tdcli.unpinChannelMessage(chat_id, 1) 862 | end 863 | if text == "help" and is_momod(msg) then 864 | help = [[ 865 | به زودی....!]] 866 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, help, 1, 'md') 867 | end 868 | if text == "del" and is_momod(msg) then 869 | tdcli.deleteMessages(chat_id, {[0] = msg.id_}) 870 | tdcli.deleteMessages(chat_id,{[0] = reply_id}) 871 | end 872 | if text == "gpinfo" and is_momod(msg) then 873 | function info(arg,data) 874 | -- vardump(data) 875 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, "اعطلاعات گروه\n*تعداد ادمین ها : *"..data.administrator_count_.."\n*تعداد ریمو شده ها : *"..data.kicked_count_.."\n*تعداد اعضا : *"..data.member_count_.."", 1, 'md') 876 | end 877 | tdcli.getChannelFull(chat_id, info, nil) 878 | end 879 | if text and text:match("^getpro (.*)$") then 880 | profilematches = {string.match(text, "^getpro (.*)$")} 881 | local function dl_photo(arg,data) 882 | tdcli.sendPhoto(msg.chat_id_, msg.id_, 0, 1, nil, data.photos_[0].sizes_[1].photo_.persistent_id_, nil) 883 | end 884 | tdcli.getUserProfilePhotos(user_id, profilematches[1] - 1, profilematches[1], dl_photo, nil) 885 | end 886 | if text and text:match('^setfloodtime (.*)$') and is_owner(msg) then 887 | redis:set('floodTime',text:match('setfloodtime (.*)')) 888 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, '_حداکثر زمان اسپم تنظیم شد به_ : *'..text:match('setfloodtime (.*)')..'*', 1, 'md') 889 | end 890 | if text and text:match('^setflood (.*)$') and is_owner(msg) then 891 | redis:set('floodMax',text:match('setflood (.*)')) 892 | tdcli.sendMessage(msg.chat_id_, msg.id_, 1, '_حداکثر تعداد اسپم تنظیم شد به_ : *'..text:match('setflood (.*)')..'*', 1, 'md') 893 | end 894 | if text == "id" then 895 | function dl_photo(arg,data) 896 | local text = 'ایدی گروه : ['..msg.chat_id_:gsub('-100','').."]\nایدی شما : ["..msg.sender_user_id_.."]\nتعداد عکس های شما : ["..data.total_count_.."]" 897 | tdcli.sendPhoto(msg.chat_id_, msg.id_, 0, 1, nil, data.photos_[0].sizes_[1].photo_.persistent_id_, text) 898 | end 899 | tdcli.getUserProfilePhotos(user_id, 0, 1, dl_photo, nil) 900 | end 901 | end 902 | function tdcli_update_callback(data) 903 | if (data.ID == "UpdateNewMessage") then 904 | run(data) 905 | elseif data.ID == "UpdateMessageEdited" then 906 | local function edited_cb(arg, data) 907 | run(data,true) 908 | end 909 | getMessage(data.chat_id_, data.message_id_, edited_cb, nil) 910 | elseif (data.ID == "UpdateOption" and data.name_ == "my_id") then 911 | tdcli_function ({ID="GetChats", offset_order_="9223372036854775807", offset_chat_id_=0, limit_=20}, dl_cb, nil) 912 | end 913 | end -------------------------------------------------------------------------------- /libs/lua-redis.lua: -------------------------------------------------------------------------------- 1 | local redis = { 2 | _VERSION = 'redis-lua 2.0.4', 3 | _DESCRIPTION = 'A Lua client library for the redis key value storage system.', 4 | _COPYRIGHT = 'Copyright (C) 2009-2012 Daniele Alessandri', 5 | } 6 | 7 | -- The following line is used for backwards compatibility in order to keep the `Redis` 8 | -- global module name. Using `Redis` is now deprecated so you should explicitly assign 9 | -- the module to a local variable when requiring it: `local redis = require('redis')`. 10 | Redis = redis 11 | 12 | local unpack = _G.unpack or table.unpack 13 | local network, request, response = {}, {}, {} 14 | 15 | local defaults = { 16 | host = '127.0.0.1', 17 | port = 6379, 18 | tcp_nodelay = true, 19 | path = nil 20 | } 21 | 22 | local function merge_defaults(parameters) 23 | if parameters == nil then 24 | parameters = {} 25 | end 26 | for k, v in pairs(defaults) do 27 | if parameters[k] == nil then 28 | parameters[k] = defaults[k] 29 | end 30 | end 31 | return parameters 32 | end 33 | 34 | local function parse_boolean(v) 35 | if v == '1' or v == 'true' or v == 'TRUE' then 36 | return true 37 | elseif v == '0' or v == 'false' or v == 'FALSE' then 38 | return false 39 | else 40 | return nil 41 | end 42 | end 43 | 44 | local function toboolean(value) return value == 1 end 45 | 46 | local function sort_request(client, command, key, params) 47 | --[[ params = { 48 | by = 'weight_*', 49 | get = 'object_*', 50 | limit = { 0, 10 }, 51 | sort = 'desc', 52 | alpha = true, 53 | } ]] 54 | local query = { key } 55 | 56 | if params then 57 | if params.by then 58 | table.insert(query, 'BY') 59 | table.insert(query, params.by) 60 | end 61 | 62 | if type(params.limit) == 'table' then 63 | -- TODO: check for lower and upper limits 64 | table.insert(query, 'LIMIT') 65 | table.insert(query, params.limit[1]) 66 | table.insert(query, params.limit[2]) 67 | end 68 | 69 | if params.get then 70 | if (type(params.get) == 'table') then 71 | for _, getarg in pairs(params.get) do 72 | table.insert(query, 'GET') 73 | table.insert(query, getarg) 74 | end 75 | else 76 | table.insert(query, 'GET') 77 | table.insert(query, params.get) 78 | end 79 | end 80 | 81 | if params.sort then 82 | table.insert(query, params.sort) 83 | end 84 | 85 | if params.alpha == true then 86 | table.insert(query, 'ALPHA') 87 | end 88 | 89 | if params.store then 90 | table.insert(query, 'STORE') 91 | table.insert(query, params.store) 92 | end 93 | end 94 | 95 | request.multibulk(client, command, query) 96 | end 97 | 98 | local function zset_range_request(client, command, ...) 99 | local args, opts = {...}, { } 100 | 101 | if #args >= 1 and type(args[#args]) == 'table' then 102 | local options = table.remove(args, #args) 103 | if options.withscores then 104 | table.insert(opts, 'WITHSCORES') 105 | end 106 | end 107 | 108 | for _, v in pairs(opts) do table.insert(args, v) end 109 | request.multibulk(client, command, args) 110 | end 111 | 112 | local function zset_range_byscore_request(client, command, ...) 113 | local args, opts = {...}, { } 114 | 115 | if #args >= 1 and type(args[#args]) == 'table' then 116 | local options = table.remove(args, #args) 117 | if options.limit then 118 | table.insert(opts, 'LIMIT') 119 | table.insert(opts, options.limit.offset or options.limit[1]) 120 | table.insert(opts, options.limit.count or options.limit[2]) 121 | end 122 | if options.withscores then 123 | table.insert(opts, 'WITHSCORES') 124 | end 125 | end 126 | 127 | for _, v in pairs(opts) do table.insert(args, v) end 128 | request.multibulk(client, command, args) 129 | end 130 | 131 | local function zset_range_reply(reply, command, ...) 132 | local args = {...} 133 | local opts = args[4] 134 | if opts and (opts.withscores or string.lower(tostring(opts)) == 'withscores') then 135 | local new_reply = { } 136 | for i = 1, #reply, 2 do 137 | table.insert(new_reply, { reply[i], reply[i + 1] }) 138 | end 139 | return new_reply 140 | else 141 | return reply 142 | end 143 | end 144 | 145 | local function zset_store_request(client, command, ...) 146 | local args, opts = {...}, { } 147 | 148 | if #args >= 1 and type(args[#args]) == 'table' then 149 | local options = table.remove(args, #args) 150 | if options.weights and type(options.weights) == 'table' then 151 | table.insert(opts, 'WEIGHTS') 152 | for _, weight in ipairs(options.weights) do 153 | table.insert(opts, weight) 154 | end 155 | end 156 | if options.aggregate then 157 | table.insert(opts, 'AGGREGATE') 158 | table.insert(opts, options.aggregate) 159 | end 160 | end 161 | 162 | for _, v in pairs(opts) do table.insert(args, v) end 163 | request.multibulk(client, command, args) 164 | end 165 | 166 | local function mset_filter_args(client, command, ...) 167 | local args, arguments = {...}, {} 168 | if (#args == 1 and type(args[1]) == 'table') then 169 | for k,v in pairs(args[1]) do 170 | table.insert(arguments, k) 171 | table.insert(arguments, v) 172 | end 173 | else 174 | arguments = args 175 | end 176 | request.multibulk(client, command, arguments) 177 | end 178 | 179 | local function hash_multi_request_builder(builder_callback) 180 | return function(client, command, ...) 181 | local args, arguments = {...}, { } 182 | if #args == 2 then 183 | table.insert(arguments, args[1]) 184 | for k, v in pairs(args[2]) do 185 | builder_callback(arguments, k, v) 186 | end 187 | else 188 | arguments = args 189 | end 190 | request.multibulk(client, command, arguments) 191 | end 192 | end 193 | 194 | local function parse_info(response) 195 | local info = {} 196 | local current = info 197 | 198 | response:gsub('([^\r\n]*)\r\n', function(kv) 199 | if kv == '' then return end 200 | 201 | local section = kv:match('^# (%w+)$') 202 | if section then 203 | current = {} 204 | info[section:lower()] = current 205 | return 206 | end 207 | 208 | local k,v = kv:match(('([^:]*):([^:]*)'):rep(1)) 209 | if k:match('db%d+') then 210 | current[k] = {} 211 | v:gsub(',', function(dbkv) 212 | local dbk,dbv = kv:match('([^:]*)=([^:]*)') 213 | current[k][dbk] = dbv 214 | end) 215 | else 216 | current[k] = v 217 | end 218 | end) 219 | 220 | return info 221 | end 222 | 223 | local function load_methods(proto, commands) 224 | local client = setmetatable ({}, getmetatable(proto)) 225 | 226 | for cmd, fn in pairs(commands) do 227 | if type(fn) ~= 'function' then 228 | redis.error('invalid type for command ' .. cmd .. '(must be a function)') 229 | end 230 | client[cmd] = fn 231 | end 232 | 233 | for i, v in pairs(proto) do 234 | client[i] = v 235 | end 236 | 237 | return client 238 | end 239 | 240 | local function create_client(proto, client_socket, commands) 241 | local client = load_methods(proto, commands) 242 | client.error = redis.error 243 | client.network = { 244 | socket = client_socket, 245 | read = network.read, 246 | write = network.write, 247 | } 248 | client.requests = { 249 | multibulk = request.multibulk, 250 | } 251 | return client 252 | end 253 | 254 | -- ############################################################################ 255 | 256 | function network.write(client, buffer) 257 | local _, err = client.network.socket:send(buffer) 258 | if err then client.error(err) end 259 | end 260 | 261 | function network.read(client, len) 262 | if len == nil then len = '*l' end 263 | local line, err = client.network.socket:receive(len) 264 | if not err then return line else client.error('connection error: ' .. err) end 265 | end 266 | 267 | -- ############################################################################ 268 | 269 | function response.read(client) 270 | local payload = client.network.read(client) 271 | local prefix, data = payload:sub(1, -#payload), payload:sub(2) 272 | 273 | -- status reply 274 | if prefix == '+' then 275 | if data == 'OK' then 276 | return true 277 | elseif data == 'QUEUED' then 278 | return { queued = true } 279 | else 280 | return data 281 | end 282 | 283 | -- error reply 284 | elseif prefix == '-' then 285 | return client.error('redis error: ' .. data) 286 | 287 | -- integer reply 288 | elseif prefix == ':' then 289 | local number = tonumber(data) 290 | 291 | if not number then 292 | if res == 'nil' then 293 | return nil 294 | end 295 | client.error('cannot parse '..res..' as a numeric response.') 296 | end 297 | 298 | return number 299 | 300 | -- bulk reply 301 | elseif prefix == '$' then 302 | local length = tonumber(data) 303 | 304 | if not length then 305 | client.error('cannot parse ' .. length .. ' as data length') 306 | end 307 | 308 | if length == -1 then 309 | return nil 310 | end 311 | 312 | local nextchunk = client.network.read(client, length + 2) 313 | 314 | return nextchunk:sub(1, -3) 315 | 316 | -- multibulk reply 317 | elseif prefix == '*' then 318 | local count = tonumber(data) 319 | 320 | if count == -1 then 321 | return nil 322 | end 323 | 324 | local list = {} 325 | if count > 0 then 326 | local reader = response.read 327 | for i = 1, count do 328 | list[i] = reader(client) 329 | end 330 | end 331 | return list 332 | 333 | -- unknown type of reply 334 | else 335 | return client.error('unknown response prefix: ' .. prefix) 336 | end 337 | end 338 | 339 | -- ############################################################################ 340 | 341 | function request.raw(client, buffer) 342 | local bufferType = type(buffer) 343 | 344 | if bufferType == 'table' then 345 | client.network.write(client, table.concat(buffer)) 346 | elseif bufferType == 'string' then 347 | client.network.write(client, buffer) 348 | else 349 | client.error('argument error: ' .. bufferType) 350 | end 351 | end 352 | 353 | function request.multibulk(client, command, ...) 354 | local args = {...} 355 | local argsn = #args 356 | local buffer = { true, true } 357 | 358 | if argsn == 1 and type(args[1]) == 'table' then 359 | argsn, args = #args[1], args[1] 360 | end 361 | 362 | buffer[1] = '*' .. tostring(argsn + 1) .. "\r\n" 363 | buffer[2] = '$' .. #command .. "\r\n" .. command .. "\r\n" 364 | 365 | local table_insert = table.insert 366 | for _, argument in pairs(args) do 367 | local s_argument = tostring(argument) 368 | table_insert(buffer, '$' .. #s_argument .. "\r\n" .. s_argument .. "\r\n") 369 | end 370 | 371 | client.network.write(client, table.concat(buffer)) 372 | end 373 | 374 | -- ############################################################################ 375 | 376 | local function custom(command, send, parse) 377 | command = string.upper(command) 378 | return function(client, ...) 379 | send(client, command, ...) 380 | local reply = response.read(client) 381 | 382 | if type(reply) == 'table' and reply.queued then 383 | reply.parser = parse 384 | return reply 385 | else 386 | if parse then 387 | return parse(reply, command, ...) 388 | end 389 | return reply 390 | end 391 | end 392 | end 393 | 394 | local function command(command, opts) 395 | if opts == nil or type(opts) == 'function' then 396 | return custom(command, request.multibulk, opts) 397 | else 398 | return custom(command, opts.request or request.multibulk, opts.response) 399 | end 400 | end 401 | 402 | local define_command_impl = function(target, name, opts) 403 | local opts = opts or {} 404 | target[string.lower(name)] = custom( 405 | opts.command or string.upper(name), 406 | opts.request or request.multibulk, 407 | opts.response or nil 408 | ) 409 | end 410 | 411 | local undefine_command_impl = function(target, name) 412 | target[string.lower(name)] = nil 413 | end 414 | 415 | -- ############################################################################ 416 | 417 | local client_prototype = {} 418 | 419 | client_prototype.raw_cmd = function(client, buffer) 420 | request.raw(client, buffer .. "\r\n") 421 | return response.read(client) 422 | end 423 | 424 | -- obsolete 425 | client_prototype.define_command = function(client, name, opts) 426 | define_command_impl(client, name, opts) 427 | end 428 | 429 | -- obsolete 430 | client_prototype.undefine_command = function(client, name) 431 | undefine_command_impl(client, name) 432 | end 433 | 434 | client_prototype.quit = function(client) 435 | request.multibulk(client, 'QUIT') 436 | client.network.socket:shutdown() 437 | return true 438 | end 439 | 440 | client_prototype.shutdown = function(client) 441 | request.multibulk(client, 'SHUTDOWN') 442 | client.network.socket:shutdown() 443 | end 444 | 445 | -- Command pipelining 446 | 447 | client_prototype.pipeline = function(client, block) 448 | local requests, replies, parsers = {}, {}, {} 449 | local table_insert = table.insert 450 | local socket_write, socket_read = client.network.write, client.network.read 451 | 452 | client.network.write = function(_, buffer) 453 | table_insert(requests, buffer) 454 | end 455 | 456 | -- TODO: this hack is necessary to temporarily reuse the current 457 | -- request -> response handling implementation of redis-lua 458 | -- without further changes in the code, but it will surely 459 | -- disappear when the new command-definition infrastructure 460 | -- will finally be in place. 461 | client.network.read = function() return '+QUEUED' end 462 | 463 | local pipeline = setmetatable({}, { 464 | __index = function(env, name) 465 | local cmd = client[name] 466 | if not cmd then 467 | client.error('unknown redis command: ' .. name, 2) 468 | end 469 | return function(self, ...) 470 | local reply = cmd(client, ...) 471 | table_insert(parsers, #requests, reply.parser) 472 | return reply 473 | end 474 | end 475 | }) 476 | 477 | local success, retval = pcall(block, pipeline) 478 | 479 | client.network.write, client.network.read = socket_write, socket_read 480 | if not success then client.error(retval, 0) end 481 | 482 | client.network.write(client, table.concat(requests, '')) 483 | 484 | for i = 1, #requests do 485 | local reply, parser = response.read(client), parsers[i] 486 | if parser then 487 | reply = parser(reply) 488 | end 489 | table_insert(replies, i, reply) 490 | end 491 | 492 | return replies, #requests 493 | end 494 | 495 | -- Publish/Subscribe 496 | 497 | do 498 | local channels = function(channels) 499 | if type(channels) == 'string' then 500 | channels = { channels } 501 | end 502 | return channels 503 | end 504 | 505 | local subscribe = function(client, ...) 506 | request.multibulk(client, 'subscribe', ...) 507 | end 508 | local psubscribe = function(client, ...) 509 | request.multibulk(client, 'psubscribe', ...) 510 | end 511 | local unsubscribe = function(client, ...) 512 | request.multibulk(client, 'unsubscribe') 513 | end 514 | local punsubscribe = function(client, ...) 515 | request.multibulk(client, 'punsubscribe') 516 | end 517 | 518 | local consumer_loop = function(client) 519 | local aborting, subscriptions = false, 0 520 | 521 | local abort = function() 522 | if not aborting then 523 | unsubscribe(client) 524 | punsubscribe(client) 525 | aborting = true 526 | end 527 | end 528 | 529 | return coroutine.wrap(function() 530 | while true do 531 | local message 532 | local response = response.read(client) 533 | 534 | if response[1] == 'pmessage' then 535 | message = { 536 | kind = response[1], 537 | pattern = response[2], 538 | channel = response[3], 539 | payload = response[4], 540 | } 541 | else 542 | message = { 543 | kind = response[1], 544 | channel = response[2], 545 | payload = response[3], 546 | } 547 | end 548 | 549 | if string.match(message.kind, '^p?subscribe$') then 550 | subscriptions = subscriptions + 1 551 | end 552 | if string.match(message.kind, '^p?unsubscribe$') then 553 | subscriptions = subscriptions - 1 554 | end 555 | 556 | if aborting and subscriptions == 0 then 557 | break 558 | end 559 | coroutine.yield(message, abort) 560 | end 561 | end) 562 | end 563 | 564 | client_prototype.pubsub = function(client, subscriptions) 565 | if type(subscriptions) == 'table' then 566 | if subscriptions.subscribe then 567 | subscribe(client, channels(subscriptions.subscribe)) 568 | end 569 | if subscriptions.psubscribe then 570 | psubscribe(client, channels(subscriptions.psubscribe)) 571 | end 572 | end 573 | return consumer_loop(client) 574 | end 575 | end 576 | 577 | -- Redis transactions (MULTI/EXEC) 578 | 579 | do 580 | local function identity(...) return ... end 581 | local emptytable = {} 582 | 583 | local function initialize_transaction(client, options, block, queued_parsers) 584 | local table_insert = table.insert 585 | local coro = coroutine.create(block) 586 | 587 | if options.watch then 588 | local watch_keys = {} 589 | for _, key in pairs(options.watch) do 590 | table_insert(watch_keys, key) 591 | end 592 | if #watch_keys > 0 then 593 | client:watch(unpack(watch_keys)) 594 | end 595 | end 596 | 597 | local transaction_client = setmetatable({}, {__index=client}) 598 | transaction_client.exec = function(...) 599 | client.error('cannot use EXEC inside a transaction block') 600 | end 601 | transaction_client.multi = function(...) 602 | coroutine.yield() 603 | end 604 | transaction_client.commands_queued = function() 605 | return #queued_parsers 606 | end 607 | 608 | assert(coroutine.resume(coro, transaction_client)) 609 | 610 | transaction_client.multi = nil 611 | transaction_client.discard = function(...) 612 | local reply = client:discard() 613 | for i, v in pairs(queued_parsers) do 614 | queued_parsers[i]=nil 615 | end 616 | coro = initialize_transaction(client, options, block, queued_parsers) 617 | return reply 618 | end 619 | transaction_client.watch = function(...) 620 | client.error('WATCH inside MULTI is not allowed') 621 | end 622 | setmetatable(transaction_client, { __index = function(t, k) 623 | local cmd = client[k] 624 | if type(cmd) == "function" then 625 | local function queuey(self, ...) 626 | local reply = cmd(client, ...) 627 | assert((reply or emptytable).queued == true, 'a QUEUED reply was expected') 628 | table_insert(queued_parsers, reply.parser or identity) 629 | return reply 630 | end 631 | t[k]=queuey 632 | return queuey 633 | else 634 | return cmd 635 | end 636 | end 637 | }) 638 | client:multi() 639 | return coro 640 | end 641 | 642 | local function transaction(client, options, coroutine_block, attempts) 643 | local queued_parsers, replies = {}, {} 644 | local retry = tonumber(attempts) or tonumber(options.retry) or 2 645 | local coro = initialize_transaction(client, options, coroutine_block, queued_parsers) 646 | 647 | local success, retval 648 | if coroutine.status(coro) == 'suspended' then 649 | success, retval = coroutine.resume(coro) 650 | else 651 | -- do not fail if the coroutine has not been resumed (missing t:multi() with CAS) 652 | success, retval = true, 'empty transaction' 653 | end 654 | if #queued_parsers == 0 or not success then 655 | client:discard() 656 | assert(success, retval) 657 | return replies, 0 658 | end 659 | 660 | local raw_replies = client:exec() 661 | if not raw_replies then 662 | if (retry or 0) <= 0 then 663 | client.error("MULTI/EXEC transaction aborted by the server") 664 | else 665 | --we're not quite done yet 666 | return transaction(client, options, coroutine_block, retry - 1) 667 | end 668 | end 669 | 670 | local table_insert = table.insert 671 | for i, parser in pairs(queued_parsers) do 672 | table_insert(replies, i, parser(raw_replies[i])) 673 | end 674 | 675 | return replies, #queued_parsers 676 | end 677 | 678 | client_prototype.transaction = function(client, arg1, arg2) 679 | local options, block 680 | if not arg2 then 681 | options, block = {}, arg1 682 | elseif arg1 then --and arg2, implicitly 683 | options, block = type(arg1)=="table" and arg1 or { arg1 }, arg2 684 | else 685 | client.error("Invalid parameters for redis transaction.") 686 | end 687 | 688 | if not options.watch then 689 | watch_keys = { } 690 | for i, v in pairs(options) do 691 | if tonumber(i) then 692 | table.insert(watch_keys, v) 693 | options[i] = nil 694 | end 695 | end 696 | options.watch = watch_keys 697 | elseif not (type(options.watch) == 'table') then 698 | options.watch = { options.watch } 699 | end 700 | 701 | if not options.cas then 702 | local tx_block = block 703 | block = function(client, ...) 704 | client:multi() 705 | return tx_block(client, ...) --can't wrap this in pcall because we're in a coroutine. 706 | end 707 | end 708 | 709 | return transaction(client, options, block) 710 | end 711 | end 712 | 713 | -- MONITOR context 714 | 715 | do 716 | local monitor_loop = function(client) 717 | local monitoring = true 718 | 719 | -- Tricky since the payload format changed starting from Redis 2.6. 720 | local pattern = '^(%d+%.%d+)( ?.- ?) ?"(%a+)" ?(.-)$' 721 | 722 | local abort = function() 723 | monitoring = false 724 | end 725 | 726 | return coroutine.wrap(function() 727 | client:monitor() 728 | 729 | while monitoring do 730 | local message, matched 731 | local response = response.read(client) 732 | 733 | local ok = response:gsub(pattern, function(time, info, cmd, args) 734 | message = { 735 | timestamp = tonumber(time), 736 | client = info:match('%d+.%d+.%d+.%d+:%d+'), 737 | database = tonumber(info:match('%d+')) or 0, 738 | command = cmd, 739 | arguments = args:match('.+'), 740 | } 741 | matched = true 742 | end) 743 | 744 | if not matched then 745 | client.error('Unable to match MONITOR payload: '..response) 746 | end 747 | 748 | coroutine.yield(message, abort) 749 | end 750 | end) 751 | end 752 | 753 | client_prototype.monitor_messages = function(client) 754 | return monitor_loop(client) 755 | end 756 | end 757 | 758 | -- ############################################################################ 759 | 760 | local function connect_tcp(socket, parameters) 761 | local host, port = parameters.host, tonumber(parameters.port) 762 | local ok, err = socket:connect(host, port) 763 | if not ok then 764 | redis.error('could not connect to '..host..':'..port..' ['..err..']') 765 | end 766 | socket:setoption('tcp-nodelay', parameters.tcp_nodelay) 767 | return socket 768 | end 769 | 770 | local function connect_unix(socket, parameters) 771 | local ok, err = socket:connect(parameters.path) 772 | if not ok then 773 | redis.error('could not connect to '..parameters.path..' ['..err..']') 774 | end 775 | return socket 776 | end 777 | 778 | local function create_connection(parameters) 779 | if parameters.socket then 780 | return parameters.socket 781 | end 782 | 783 | local perform_connection, socket 784 | 785 | if parameters.scheme == 'unix' then 786 | perform_connection, socket = connect_unix, require('socket.unix') 787 | assert(socket, 'your build of LuaSocket does not support UNIX domain sockets') 788 | else 789 | if parameters.scheme then 790 | local scheme = parameters.scheme 791 | assert(scheme == 'redis' or scheme == 'tcp', 'invalid scheme: '..scheme) 792 | end 793 | perform_connection, socket = connect_tcp, require('socket').tcp 794 | end 795 | 796 | return perform_connection(socket(), parameters) 797 | end 798 | 799 | -- ############################################################################ 800 | 801 | function redis.error(message, level) 802 | error(message, (level or 1) + 1) 803 | end 804 | 805 | function redis.connect(...) 806 | local args, parameters = {...}, nil 807 | 808 | if #args == 1 then 809 | if type(args[1]) == 'table' then 810 | parameters = args[1] 811 | else 812 | local uri = require('socket.url') 813 | parameters = uri.parse(select(1, ...)) 814 | if parameters.scheme then 815 | if parameters.query then 816 | for k, v in parameters.query:gmatch('([-_%w]+)=([-_%w]+)') do 817 | if k == 'tcp_nodelay' or k == 'tcp-nodelay' then 818 | parameters.tcp_nodelay = parse_boolean(v) 819 | end 820 | end 821 | end 822 | else 823 | parameters.host = parameters.path 824 | end 825 | end 826 | elseif #args > 1 then 827 | local host, port = unpack(args) 828 | parameters = { host = host, port = port } 829 | end 830 | 831 | local commands = redis.commands or {} 832 | if type(commands) ~= 'table' then 833 | redis.error('invalid type for the commands table') 834 | end 835 | 836 | local socket = create_connection(merge_defaults(parameters)) 837 | local client = create_client(client_prototype, socket, commands) 838 | 839 | return client 840 | end 841 | 842 | function redis.command(cmd, opts) 843 | return command(cmd, opts) 844 | end 845 | 846 | -- obsolete 847 | function redis.define_command(name, opts) 848 | define_command_impl(redis.commands, name, opts) 849 | end 850 | 851 | -- obsolete 852 | function redis.undefine_command(name) 853 | undefine_command_impl(redis.commands, name) 854 | end 855 | 856 | -- ############################################################################ 857 | 858 | -- Commands defined in this table do not take the precedence over 859 | -- methods defined in the client prototype table. 860 | 861 | redis.commands = { 862 | -- commands operating on the key space 863 | exists = command('EXISTS', { 864 | response = toboolean 865 | }), 866 | del = command('DEL'), 867 | type = command('TYPE'), 868 | rename = command('RENAME'), 869 | renamenx = command('RENAMENX', { 870 | response = toboolean 871 | }), 872 | expire = command('EXPIRE', { 873 | response = toboolean 874 | }), 875 | pexpire = command('PEXPIRE', { -- >= 2.6 876 | response = toboolean 877 | }), 878 | expireat = command('EXPIREAT', { 879 | response = toboolean 880 | }), 881 | pexpireat = command('PEXPIREAT', { -- >= 2.6 882 | response = toboolean 883 | }), 884 | ttl = command('TTL'), 885 | pttl = command('PTTL'), -- >= 2.6 886 | move = command('MOVE', { 887 | response = toboolean 888 | }), 889 | dbsize = command('DBSIZE'), 890 | persist = command('PERSIST', { -- >= 2.2 891 | response = toboolean 892 | }), 893 | keys = command('KEYS', { 894 | response = function(response) 895 | if type(response) == 'string' then 896 | -- backwards compatibility path for Redis < 2.0 897 | local keys = {} 898 | response:gsub('[^%s]+', function(key) 899 | table.insert(keys, key) 900 | end) 901 | response = keys 902 | end 903 | return response 904 | end 905 | }), 906 | randomkey = command('RANDOMKEY', { 907 | response = function(response) 908 | if response == '' then 909 | return nil 910 | else 911 | return response 912 | end 913 | end 914 | }), 915 | sort = command('SORT', { 916 | request = sort_request, 917 | }), 918 | 919 | -- commands operating on string values 920 | set = command('SET'), 921 | setnx = command('SETNX', { 922 | response = toboolean 923 | }), 924 | setex = command('SETEX'), -- >= 2.0 925 | psetex = command('PSETEX'), -- >= 2.6 926 | mset = command('MSET', { 927 | request = mset_filter_args 928 | }), 929 | msetnx = command('MSETNX', { 930 | request = mset_filter_args, 931 | response = toboolean 932 | }), 933 | get = command('GET'), 934 | mget = command('MGET'), 935 | getset = command('GETSET'), 936 | incr = command('INCR'), 937 | incrby = command('INCRBY'), 938 | incrbyfloat = command('INCRBYFLOAT', { -- >= 2.6 939 | response = function(reply, command, ...) 940 | return tonumber(reply) 941 | end, 942 | }), 943 | decr = command('DECR'), 944 | decrby = command('DECRBY'), 945 | append = command('APPEND'), -- >= 2.0 946 | substr = command('SUBSTR'), -- >= 2.0 947 | strlen = command('STRLEN'), -- >= 2.2 948 | setrange = command('SETRANGE'), -- >= 2.2 949 | getrange = command('GETRANGE'), -- >= 2.2 950 | setbit = command('SETBIT'), -- >= 2.2 951 | getbit = command('GETBIT'), -- >= 2.2 952 | 953 | -- commands operating on lists 954 | rpush = command('RPUSH'), 955 | lpush = command('LPUSH'), 956 | llen = command('LLEN'), 957 | lrange = command('LRANGE'), 958 | ltrim = command('LTRIM'), 959 | lindex = command('LINDEX'), 960 | lset = command('LSET'), 961 | lrem = command('LREM'), 962 | lpop = command('LPOP'), 963 | rpop = command('RPOP'), 964 | rpoplpush = command('RPOPLPUSH'), 965 | blpop = command('BLPOP'), -- >= 2.0 966 | brpop = command('BRPOP'), -- >= 2.0 967 | rpushx = command('RPUSHX'), -- >= 2.2 968 | lpushx = command('LPUSHX'), -- >= 2.2 969 | linsert = command('LINSERT'), -- >= 2.2 970 | brpoplpush = command('BRPOPLPUSH'), -- >= 2.2 971 | 972 | -- commands operating on sets 973 | sadd = command('SADD'), 974 | srem = command('SREM'), 975 | spop = command('SPOP'), 976 | smove = command('SMOVE', { 977 | response = toboolean 978 | }), 979 | scard = command('SCARD'), 980 | sismember = command('SISMEMBER', { 981 | response = toboolean 982 | }), 983 | sinter = command('SINTER'), 984 | sinterstore = command('SINTERSTORE'), 985 | sunion = command('SUNION'), 986 | sunionstore = command('SUNIONSTORE'), 987 | sdiff = command('SDIFF'), 988 | sdiffstore = command('SDIFFSTORE'), 989 | smembers = command('SMEMBERS'), 990 | srandmember = command('SRANDMEMBER'), 991 | 992 | -- commands operating on sorted sets 993 | zadd = command('ZADD'), 994 | zincrby = command('ZINCRBY'), 995 | zrem = command('ZREM'), 996 | zrange = command('ZRANGE', { 997 | request = zset_range_request, 998 | response = zset_range_reply, 999 | }), 1000 | zrevrange = command('ZREVRANGE', { 1001 | request = zset_range_request, 1002 | response = zset_range_reply, 1003 | }), 1004 | zrangebyscore = command('ZRANGEBYSCORE', { 1005 | request = zset_range_byscore_request, 1006 | response = zset_range_reply, 1007 | }), 1008 | zrevrangebyscore = command('ZREVRANGEBYSCORE', { -- >= 2.2 1009 | request = zset_range_byscore_request, 1010 | response = zset_range_reply, 1011 | }), 1012 | zunionstore = command('ZUNIONSTORE', { -- >= 2.0 1013 | request = zset_store_request 1014 | }), 1015 | zinterstore = command('ZINTERSTORE', { -- >= 2.0 1016 | request = zset_store_request 1017 | }), 1018 | zcount = command('ZCOUNT'), 1019 | zcard = command('ZCARD'), 1020 | zscore = command('ZSCORE'), 1021 | zremrangebyscore = command('ZREMRANGEBYSCORE'), 1022 | zrank = command('ZRANK'), -- >= 2.0 1023 | zrevrank = command('ZREVRANK'), -- >= 2.0 1024 | zremrangebyrank = command('ZREMRANGEBYRANK'), -- >= 2.0 1025 | 1026 | -- commands operating on hashes 1027 | hset = command('HSET', { -- >= 2.0 1028 | response = toboolean 1029 | }), 1030 | hsetnx = command('HSETNX', { -- >= 2.0 1031 | response = toboolean 1032 | }), 1033 | hmset = command('HMSET', { -- >= 2.0 1034 | request = hash_multi_request_builder(function(args, k, v) 1035 | table.insert(args, k) 1036 | table.insert(args, v) 1037 | end), 1038 | }), 1039 | hincrby = command('HINCRBY'), -- >= 2.0 1040 | hincrbyfloat = command('HINCRBYFLOAT', {-- >= 2.6 1041 | response = function(reply, command, ...) 1042 | return tonumber(reply) 1043 | end, 1044 | }), 1045 | hget = command('HGET'), -- >= 2.0 1046 | hmget = command('HMGET', { -- >= 2.0 1047 | request = hash_multi_request_builder(function(args, k, v) 1048 | table.insert(args, v) 1049 | end), 1050 | }), 1051 | hdel = command('HDEL'), -- >= 2.0 1052 | hexists = command('HEXISTS', { -- >= 2.0 1053 | response = toboolean 1054 | }), 1055 | hlen = command('HLEN'), -- >= 2.0 1056 | hkeys = command('HKEYS'), -- >= 2.0 1057 | hvals = command('HVALS'), -- >= 2.0 1058 | hgetall = command('HGETALL', { -- >= 2.0 1059 | response = function(reply, command, ...) 1060 | local new_reply = { } 1061 | for i = 1, #reply, 2 do new_reply[reply[i]] = reply[i + 1] end 1062 | return new_reply 1063 | end 1064 | }), 1065 | 1066 | -- connection related commands 1067 | ping = command('PING', { 1068 | response = function(response) return response == 'PONG' end 1069 | }), 1070 | echo = command('ECHO'), 1071 | auth = command('AUTH'), 1072 | select = command('SELECT'), 1073 | 1074 | -- transactions 1075 | multi = command('MULTI'), -- >= 2.0 1076 | exec = command('EXEC'), -- >= 2.0 1077 | discard = command('DISCARD'), -- >= 2.0 1078 | watch = command('WATCH'), -- >= 2.2 1079 | unwatch = command('UNWATCH'), -- >= 2.2 1080 | 1081 | -- publish - subscribe 1082 | subscribe = command('SUBSCRIBE'), -- >= 2.0 1083 | unsubscribe = command('UNSUBSCRIBE'), -- >= 2.0 1084 | psubscribe = command('PSUBSCRIBE'), -- >= 2.0 1085 | punsubscribe = command('PUNSUBSCRIBE'), -- >= 2.0 1086 | publish = command('PUBLISH'), -- >= 2.0 1087 | 1088 | -- redis scripting 1089 | eval = command('EVAL'), -- >= 2.6 1090 | evalsha = command('EVALSHA'), -- >= 2.6 1091 | script = command('SCRIPT'), -- >= 2.6 1092 | 1093 | -- remote server control commands 1094 | bgrewriteaof = command('BGREWRITEAOF'), 1095 | config = command('CONFIG', { -- >= 2.0 1096 | response = function(reply, command, ...) 1097 | if (type(reply) == 'table') then 1098 | local new_reply = { } 1099 | for i = 1, #reply, 2 do new_reply[reply[i]] = reply[i + 1] end 1100 | return new_reply 1101 | end 1102 | 1103 | return reply 1104 | end 1105 | }), 1106 | client = command('CLIENT'), -- >= 2.4 1107 | slaveof = command('SLAVEOF'), 1108 | save = command('SAVE'), 1109 | bgsave = command('BGSAVE'), 1110 | lastsave = command('LASTSAVE'), 1111 | flushdb = command('FLUSHDB'), 1112 | flushall = command('FLUSHALL'), 1113 | monitor = command('MONITOR'), 1114 | time = command('TIME'), -- >= 2.6 1115 | slowlog = command('SLOWLOG', { -- >= 2.2.13 1116 | response = function(reply, command, ...) 1117 | if (type(reply) == 'table') then 1118 | local structured = { } 1119 | for index, entry in ipairs(reply) do 1120 | structured[index] = { 1121 | id = tonumber(entry[1]), 1122 | timestamp = tonumber(entry[2]), 1123 | duration = tonumber(entry[3]), 1124 | command = entry[4], 1125 | } 1126 | end 1127 | return structured 1128 | end 1129 | 1130 | return reply 1131 | end 1132 | }), 1133 | info = command('INFO', { 1134 | response = parse_info, 1135 | }), 1136 | } 1137 | 1138 | -- ############################################################################ 1139 | 1140 | return redis 1141 | -------------------------------------------------------------------------------- /tdcli.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | This program is free software; you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation; either version 2 of the License, or 5 | (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with this program; if not, write to the Free Software 14 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 15 | MA 02110-1301, USA. 16 | 17 | ]]-- 18 | 19 | -- Vector example form is like this: {[0] = v} or {v1, v2, v3, [0] = v} 20 | -- If false or true crashed your telegram-cli, try to change true to 1 and false to 0 21 | 22 | -- Main table 23 | local M = {} 24 | local db = dofile 'libs/redis.lua' 25 | -- It's do nothing but suppress "lua: attempt to call a nil value" warning 26 | function dl_cb(arg, data) 27 | end 28 | 29 | -- @chat_id = user, group, channel, and broadcast 30 | -- @group_id = normal group 31 | -- @channel_id = channel and broadcast 32 | local function getChatId(chat_id) 33 | local chat = {} 34 | local chat_id = tostring(chat_id) 35 | 36 | if chat_id:match('^-100') then 37 | local channel_id = chat_id:gsub('-100', '') 38 | chat = {ID = channel_id, type = 'channel'} 39 | else 40 | local group_id = chat_id:gsub('-', '') 41 | chat = {ID = group_id, type = 'group'} 42 | end 43 | 44 | return chat 45 | end 46 | 47 | local function getInputFile(file) 48 | local input = tostring(file) 49 | 50 | if input:match('/') then 51 | infile = {ID = "InputFileLocal", path_ = file} 52 | elseif input:match('^%d+$') then 53 | infile = {ID = "InputFileId", id_ = file} 54 | else 55 | infile = {ID = "InputFilePersistentId", persistent_id_ = file} 56 | end 57 | 58 | return infile 59 | end 60 | 61 | -- User can send bold, italic, and monospace text uses HTML or Markdown format. 62 | local function getParseMode(parse_mode) 63 | if parse_mode then 64 | local mode = parse_mode:lower() 65 | 66 | if mode == 'markdown' or mode == 'md' then 67 | P = {ID = "TextParseModeMarkdown"} 68 | elseif mode == 'html' then 69 | P = {ID = "TextParseModeHTML"} 70 | end 71 | end 72 | 73 | return P 74 | end 75 | 76 | -- Send SendMessage request 77 | local function sendRequest(request_id, chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, callback, extra) 78 | tdcli_function ({ 79 | ID = request_id, 80 | chat_id_ = chat_id, 81 | reply_to_message_id_ = reply_to_message_id, 82 | disable_notification_ = disable_notification, 83 | from_background_ = from_background, 84 | reply_markup_ = reply_markup, 85 | input_message_content_ = input_message_content, 86 | }, callback or dl_cb, extra) 87 | end 88 | 89 | -- Returns current authorization state, offline request 90 | local function getAuthState(dl_cb, cmd) 91 | tdcli_function ({ 92 | ID = "GetAuthState", 93 | }, cb or dl_cb, cmd) 94 | end 95 | 96 | M.getAuthState = getAuthState 97 | 98 | -- Sets user's phone number and sends authentication code to the user. 99 | -- Works only when authGetState returns authStateWaitPhoneNumber. 100 | -- If phone number is not recognized or another error has happened, returns an error. Otherwise returns authStateWaitCode 101 | -- @phone_number User's phone number in any reasonable format 102 | -- @allow_flash_call Pass True, if code can be sent via flash call to the specified phone number 103 | -- @is_current_phone_number Pass true, if the phone number is used on the current device. Ignored if allow_flash_call is False 104 | local function setAuthPhoneNumber(phone_number, allow_flash_call, is_current_phone_number, cb, cmd) 105 | tdcli_function ({ 106 | ID = "SetAuthPhoneNumber", 107 | phone_number_ = phone_number, 108 | allow_flash_call_ = allow_flash_call, 109 | is_current_phone_number_ = is_current_phone_number 110 | }, cb or dl_cb, cmd) 111 | end 112 | 113 | M.setAuthPhoneNumber = setAuthPhoneNumber 114 | 115 | -- Resends authentication code to the user. 116 | -- Works only when authGetState returns authStateWaitCode and next_code_type of result is not null. 117 | -- Returns authStateWaitCode on success 118 | local function resendAuthCode(dl_cb, cmd) 119 | tdcli_function ({ 120 | ID = "ResendAuthCode", 121 | }, cb or dl_cb, cmd) 122 | end 123 | 124 | M.resendAuthCode = resendAuthCode 125 | 126 | -- Checks authentication code. 127 | -- Works only when authGetState returns authStateWaitCode. 128 | -- Returns authStateWaitPassword or authStateOk on success 129 | -- @code Verification code from SMS, Telegram message, voice call or flash call 130 | -- @first_name User first name, if user is yet not registered, 1-255 characters 131 | -- @last_name Optional user last name, if user is yet not registered, 0-255 characters 132 | local function checkAuthCode(code, first_name, last_name, cb, cmd) 133 | tdcli_function ({ 134 | ID = "CheckAuthCode", 135 | code_ = code, 136 | first_name_ = first_name, 137 | last_name_ = last_name 138 | }, cb or dl_cb, cmd) 139 | end 140 | 141 | M.checkAuthCode = checkAuthCode 142 | 143 | -- Checks password for correctness. 144 | -- Works only when authGetState returns authStateWaitPassword. 145 | -- Returns authStateOk on success 146 | -- @password Password to check 147 | local function checkAuthPassword(password, cb, cmd) 148 | tdcli_function ({ 149 | ID = "CheckAuthPassword", 150 | password_ = password 151 | }, cb or dl_cb, cmd) 152 | end 153 | 154 | M.checkAuthPassword = checkAuthPassword 155 | 156 | -- Requests to send password recovery code to email. 157 | -- Works only when authGetState returns authStateWaitPassword. 158 | -- Returns authStateWaitPassword on success 159 | local function requestAuthPasswordRecovery(dl_cb, cmd) 160 | tdcli_function ({ 161 | ID = "RequestAuthPasswordRecovery", 162 | }, cb or dl_cb, cmd) 163 | end 164 | 165 | M.requestAuthPasswordRecovery = requestAuthPasswordRecovery 166 | 167 | -- Recovers password with recovery code sent to email. 168 | -- Works only when authGetState returns authStateWaitPassword. 169 | -- Returns authStateOk on success 170 | -- @recovery_code Recovery code to check 171 | local function recoverAuthPassword(recovery_code, cb, cmd) 172 | tdcli_function ({ 173 | ID = "RecoverAuthPassword", 174 | recovery_code_ = recovery_code 175 | }, cb or dl_cb, cmd) 176 | end 177 | 178 | M.recoverAuthPassword = recoverAuthPassword 179 | 180 | -- Logs out user. 181 | -- If force == false, begins to perform soft log out, returns authStateLoggingOut after completion. 182 | -- If force == true then succeeds almost immediately without cleaning anything at the server, but returns error with code 401 and description "Unauthorized" 183 | -- @force If true, just delete all local data. Session will remain in list of active sessions 184 | local function resetAuth(force, cb, cmd) 185 | tdcli_function ({ 186 | ID = "ResetAuth", 187 | force_ = force or nil 188 | }, cb or dl_cb, cmd) 189 | end 190 | 191 | M.resetAuth = resetAuth 192 | 193 | -- Check bot's authentication token to log in as a bot. 194 | -- Works only when authGetState returns authStateWaitPhoneNumber. 195 | -- Can be used instead of setAuthPhoneNumber and checkAuthCode to log in. 196 | -- Returns authStateOk on success 197 | -- @token Bot token 198 | local function checkAuthBotToken(token, cb, cmd) 199 | tdcli_function ({ 200 | ID = "CheckAuthBotToken", 201 | token_ = token 202 | }, cb or dl_cb, cmd) 203 | end 204 | 205 | M.checkAuthBotToken = checkAuthBotToken 206 | 207 | -- Returns current state of two-step verification 208 | local function getPasswordState(dl_cb, cmd) 209 | tdcli_function ({ 210 | ID = "GetPasswordState", 211 | }, cb or dl_cb, cmd) 212 | end 213 | 214 | M.getPasswordState = getPasswordState 215 | 216 | -- Changes user password. 217 | -- If new recovery email is specified, then error EMAIL_UNCONFIRMED is returned and password change will not be applied until email confirmation. 218 | -- Application should call getPasswordState from time to time to check if email is already confirmed 219 | -- @old_password Old user password 220 | -- @new_password New user password, may be empty to remove the password 221 | -- @new_hint New password hint, can be empty 222 | -- @set_recovery_email Pass True, if recovery email should be changed 223 | -- @new_recovery_email New recovery email, may be empty 224 | local function setPassword(old_password, new_password, new_hint, set_recovery_email, new_recovery_email, cb, cmd) 225 | tdcli_function ({ 226 | ID = "SetPassword", 227 | old_password_ = old_password, 228 | new_password_ = new_password, 229 | new_hint_ = new_hint, 230 | set_recovery_email_ = set_recovery_email, 231 | new_recovery_email_ = new_recovery_email 232 | }, cb or dl_cb, cmd) 233 | end 234 | 235 | M.setPassword = setPassword 236 | 237 | -- Returns set up recovery email. 238 | -- This method can be used to verify a password provided by the user 239 | -- @password Current user password 240 | local function getRecoveryEmail(password, cb, cmd) 241 | tdcli_function ({ 242 | ID = "GetRecoveryEmail", 243 | password_ = password 244 | }, cb or dl_cb, cmd) 245 | end 246 | 247 | M.getRecoveryEmail = getRecoveryEmail 248 | 249 | -- Changes user recovery email. 250 | -- If new recovery email is specified, then error EMAIL_UNCONFIRMED is returned and email will not be changed until email confirmation. 251 | -- Application should call getPasswordState from time to time to check if email is already confirmed. 252 | -- If new_recovery_email coincides with the current set up email succeeds immediately and aborts all other requests waiting for email confirmation 253 | -- @password Current user password 254 | -- @new_recovery_email New recovery email 255 | local function setRecoveryEmail(password, new_recovery_email, cb, cmd) 256 | tdcli_function ({ 257 | ID = "SetRecoveryEmail", 258 | password_ = password, 259 | new_recovery_email_ = new_recovery_email 260 | }, cb or dl_cb, cmd) 261 | end 262 | 263 | M.setRecoveryEmail = setRecoveryEmail 264 | 265 | -- Requests to send password recovery code to email 266 | local function requestPasswordRecovery(dl_cb, cmd) 267 | tdcli_function ({ 268 | ID = "RequestPasswordRecovery", 269 | }, cb or dl_cb, cmd) 270 | end 271 | 272 | M.requestPasswordRecovery = requestPasswordRecovery 273 | 274 | -- Recovers password with recovery code sent to email 275 | -- @recovery_code Recovery code to check 276 | local function recoverPassword(recovery_code, cb, cmd) 277 | tdcli_function ({ 278 | ID = "RecoverPassword", 279 | recovery_code_ = tostring(recovery_code) 280 | }, cb or dl_cb, cmd) 281 | end 282 | 283 | M.recoverPassword = recoverPassword 284 | 285 | -- Returns current logged in user 286 | local function getMe(cb, cmd) 287 | tdcli_function ({ 288 | ID = "GetMe", 289 | }, cb or dl_cb, cmd) 290 | end 291 | 292 | M.getMe = getMe 293 | 294 | -- Returns information about a user by its identifier, offline request if current user is not a bot 295 | -- @user_id User identifier 296 | local function getUser(user_id, cb, cmd) 297 | tdcli_function ({ 298 | ID = "GetUser", 299 | user_id_ = user_id 300 | }, cb or dl_cb, cmd) 301 | end 302 | 303 | M.getUser = getUser 304 | 305 | -- Returns full information about a user by its identifier 306 | -- @user_id User identifier 307 | local function getUserFull(user_id, cb, cmd) 308 | tdcli_function ({ 309 | ID = "GetUserFull", 310 | user_id_ = user_id 311 | }, cb or dl_cb, cmd) 312 | end 313 | 314 | M.getUserFull = getUserFull 315 | 316 | -- Returns information about a group by its identifier, offline request if current user is not a bot 317 | -- @group_id Group identifier 318 | local function getGroup(group_id, cb, cmd) 319 | tdcli_function ({ 320 | ID = "GetGroup", 321 | group_id_ = getChatId(group_id).ID 322 | }, cb or dl_cb, cmd) 323 | end 324 | 325 | M.getGroup = getGroup 326 | 327 | -- Returns full information about a group by its identifier 328 | -- @group_id Group identifier 329 | local function getGroupFull(group_id, cb, cmd) 330 | tdcli_function ({ 331 | ID = "GetGroupFull", 332 | group_id_ = getChatId(group_id).ID 333 | }, cb or dl_cb, cmd) 334 | end 335 | 336 | M.getGroupFull = getGroupFull 337 | 338 | -- Returns information about a channel by its identifier, offline request if current user is not a bot 339 | -- @channel_id Channel identifier 340 | local function getChannel(channel_id, cb, cmd) 341 | tdcli_function ({ 342 | ID = "GetChannel", 343 | channel_id_ = getChatId(channel_id).ID 344 | }, cb or dl_cb, cmd) 345 | end 346 | 347 | M.getChannel = getChannel 348 | 349 | -- Returns full information about a channel by its identifier, cached for at most 1 minute 350 | -- @channel_id Channel identifier 351 | local function getChannelFull(channel_id, cb, cmd) 352 | tdcli_function ({ 353 | ID = "GetChannelFull", 354 | channel_id_ = getChatId(channel_id).ID 355 | }, cb or dl_cb, cmd) 356 | end 357 | 358 | M.getChannelFull = getChannelFull 359 | 360 | -- Returns information about a secret chat by its identifier, offline request 361 | -- @secret_chat_id Secret chat identifier 362 | local function getSecretChat(secret_chat_id, cb, cmd) 363 | tdcli_function ({ 364 | ID = "GetSecretChat", 365 | secret_chat_id_ = secret_chat_id 366 | }, cb or dl_cb, cmd) 367 | end 368 | 369 | M.getSecretChat = getSecretChat 370 | 371 | -- Returns information about a chat by its identifier, offline request if current user is not a bot 372 | -- @chat_id Chat identifier 373 | local function getChat(chat_id, cb, cmd) 374 | tdcli_function ({ 375 | ID = "GetChat", 376 | chat_id_ = chat_id 377 | }, cb or dl_cb, cmd) 378 | end 379 | 380 | M.getChat = getChat 381 | 382 | -- Returns information about a message 383 | -- @chat_id Identifier of the chat, message belongs to 384 | -- @message_id Identifier of the message to get 385 | local function getMessage(chat_id, message_id, cb, cmd) 386 | tdcli_function ({ 387 | ID = "GetMessage", 388 | chat_id_ = chat_id, 389 | message_id_ = message_id 390 | }, cb or dl_cb, cmd) 391 | end 392 | 393 | M.getMessage = getMessage 394 | 395 | -- Returns information about messages. 396 | -- If message is not found, returns null on the corresponding position of the result 397 | -- @chat_id Identifier of the chat, messages belongs to 398 | -- @message_ids Identifiers of the messages to get 399 | local function getMessages(chat_id, message_ids, cb, cmd) 400 | tdcli_function ({ 401 | ID = "GetMessages", 402 | chat_id_ = chat_id, 403 | message_ids_ = message_ids -- vector 404 | }, cb or dl_cb, cmd) 405 | end 406 | 407 | M.getMessages = getMessages 408 | 409 | -- Returns information about a file, offline request 410 | -- @file_id Identifier of the file to get 411 | local function getFile(file_id, cb, cmd) 412 | tdcli_function ({ 413 | ID = "GetFile", 414 | file_id_ = file_id 415 | }, cb or dl_cb, cmd) 416 | end 417 | 418 | M.getFile = getFile 419 | 420 | -- Returns information about a file by its persistent id, offline request 421 | -- @persistent_file_id Persistent identifier of the file to get 422 | local function getFilePersistent(persistent_file_id, cb, cmd) 423 | tdcli_function ({ 424 | ID = "GetFilePersistent", 425 | persistent_file_id_ = persistent_file_id 426 | }, cb or dl_cb, cmd) 427 | end 428 | 429 | M.getFilePersistent = getFilePersistent 430 | 431 | -- Returns list of chats in the right order, chats are sorted by (order, chat_id) in decreasing order. 432 | -- For example, to get list of chats from the beginning, the offset_order should be equal 2^63 - 1 433 | -- @offset_order Chat order to return chats from 434 | -- @offset_chat_id Chat identifier to return chats from 435 | -- @limit Maximum number of chats to be returned 436 | local function getChats(offset_order, offset_chat_id, limit, cb, cmd) 437 | if not limit or limit > 20 then 438 | limit = 20 439 | end 440 | 441 | tdcli_function ({ 442 | ID = "GetChats", 443 | offset_order_ = offset_order or 9223372036854775807, 444 | offset_chat_id_ = offset_chat_id or 0, 445 | limit_ = limit 446 | }, cb or dl_cb, cmd) 447 | end 448 | 449 | M.getChats = getChats 450 | 451 | -- Searches public chat by its username. 452 | -- Currently only private and channel chats can be public. 453 | -- Returns chat if found, otherwise some error is returned 454 | -- @username Username to be resolved 455 | local function searchPublicChat(username, cb, cmd) 456 | tdcli_function ({ 457 | ID = "SearchPublicChat", 458 | username_ = username 459 | }, cb or dl_cb, cmd) 460 | end 461 | 462 | M.searchPublicChat = searchPublicChat 463 | 464 | -- Searches public chats by prefix of their username. 465 | -- Currently only private and channel (including supergroup) chats can be public. 466 | -- Returns meaningful number of results. 467 | -- Returns nothing if length of the searched username prefix is less than 5. 468 | -- Excludes private chats with contacts from the results 469 | -- @username_prefix Prefix of the username to search 470 | local function searchPublicChats(username_prefix, cb, cmd) 471 | tdcli_function ({ 472 | ID = "SearchPublicChats", 473 | username_prefix_ = username_prefix 474 | }, cb or dl_cb, cmd) 475 | end 476 | 477 | M.searchPublicChats = searchPublicChats 478 | 479 | -- Searches for specified query in the title and username of known chats, offline request. 480 | -- Returns chats in the order of them in the chat list 481 | -- @query Query to search for, if query is empty, returns up to 20 recently found chats 482 | -- @limit Maximum number of chats to be returned 483 | local function searchChats(query, limit, cb, cmd) 484 | if not limit or limit > 20 then 485 | limit = 20 486 | end 487 | 488 | tdcli_function ({ 489 | ID = "SearchChats", 490 | query_ = query, 491 | limit_ = limit 492 | }, cb or dl_cb, cmd) 493 | end 494 | 495 | M.searchChats = searchChats 496 | 497 | -- Adds chat to the list of recently found chats. 498 | -- The chat is added to the beginning of the list. 499 | -- If the chat is already in the list, at first it is removed from the list 500 | -- @chat_id Identifier of the chat to add 501 | local function addRecentlyFoundChat(chat_id, cb, cmd) 502 | tdcli_function ({ 503 | ID = "AddRecentlyFoundChat", 504 | chat_id_ = chat_id 505 | }, cb or dl_cb, cmd) 506 | end 507 | 508 | M.addRecentlyFoundChat = addRecentlyFoundChat 509 | 510 | -- Deletes chat from the list of recently found chats 511 | -- @chat_id Identifier of the chat to delete 512 | local function deleteRecentlyFoundChat(chat_id, cb, cmd) 513 | tdcli_function ({ 514 | ID = "DeleteRecentlyFoundChat", 515 | chat_id_ = chat_id 516 | }, cb or dl_cb, cmd) 517 | end 518 | 519 | M.deleteRecentlyFoundChat = deleteRecentlyFoundChat 520 | 521 | -- Clears list of recently found chats 522 | local function deleteRecentlyFoundChats(dl_cb, cmd) 523 | tdcli_function ({ 524 | ID = "DeleteRecentlyFoundChats", 525 | }, cb or dl_cb, cmd) 526 | end 527 | 528 | M.deleteRecentlyFoundChats = deleteRecentlyFoundChats 529 | 530 | -- Returns list of common chats with an other given user. 531 | -- Chats are sorted by their type and creation date 532 | -- @user_id User identifier 533 | -- @offset_chat_id Chat identifier to return chats from, use 0 for the first request 534 | -- @limit Maximum number of chats to be returned, up to 100 535 | local function getCommonChats(user_id, offset_chat_id, limit, cb, cmd) 536 | if not limit or limit > 100 then 537 | limit = 100 538 | end 539 | 540 | tdcli_function ({ 541 | ID = "GetCommonChats", 542 | user_id_ = user_id, 543 | offset_chat_id_ = offset_chat_id, 544 | limit_ = limit 545 | }, cb or dl_cb, cmd) 546 | end 547 | 548 | M.getCommonChats = getCommonChats 549 | 550 | -- Returns messages in a chat. 551 | -- Automatically calls openChat. 552 | -- Returns result in reverse chronological order, i.e. in order of decreasing message.message_id 553 | -- @chat_id Chat identifier 554 | -- @from_message_id Identifier of the message near which we need a history, you can use 0 to get results from the beginning, i.e. from oldest to newest 555 | -- @offset Specify 0 to get results exactly from from_message_id or negative offset to get specified message and some newer messages 556 | -- @limit Maximum number of messages to be returned, should be positive and can't be greater than 100. 557 | -- If offset is negative, limit must be greater than -offset. 558 | -- There may be less than limit messages returned even the end of the history is not reached 559 | local function getChatHistory(chat_id, from_message_id, offset, limit, cb, cmd) 560 | if not limit or limit > 100 then 561 | limit = 100 562 | end 563 | 564 | tdcli_function ({ 565 | ID = "GetChatHistory", 566 | chat_id_ = chat_id, 567 | from_message_id_ = from_message_id, 568 | offset_ = offset or 0, 569 | limit_ = limit 570 | }, cb or dl_cb, cmd) 571 | end 572 | 573 | M.getChatHistory = getChatHistory 574 | 575 | -- Deletes all messages in the chat. 576 | -- Can't be used for channel chats 577 | -- @chat_id Chat identifier 578 | -- @remove_from_chat_list Pass true, if chat should be removed from the chat list 579 | local function deleteChatHistory(chat_id, remove_from_chat_list, cb, cmd) 580 | tdcli_function ({ 581 | ID = "DeleteChatHistory", 582 | chat_id_ = chat_id, 583 | remove_from_chat_list_ = remove_from_chat_list 584 | }, cb or dl_cb, cmd) 585 | end 586 | 587 | M.deleteChatHistory = deleteChatHistory 588 | 589 | -- Searches for messages with given words in the chat. 590 | -- Returns result in reverse chronological order, i. e. in order of decreasimg message_id. 591 | -- Doesn't work in secret chats 592 | -- @chat_id Chat identifier to search in 593 | -- @query Query to search for 594 | -- @from_message_id Identifier of the message from which we need a history, you can use 0 to get results from beginning 595 | -- @limit Maximum number of messages to be returned, can't be greater than 100 596 | -- @filter Filter for content of searched messages 597 | -- filter = Empty|Animation|Audio|Document|Photo|Video|Voice|PhotoAndVideo|Url|ChatPhoto 598 | local function searchChatMessages(chat_id, query, from_message_id, limit, filter, cb, cmd) 599 | if not limit or limit > 100 then 600 | limit = 100 601 | end 602 | 603 | tdcli_function ({ 604 | ID = "SearchChatMessages", 605 | chat_id_ = chat_id, 606 | query_ = query, 607 | from_message_id_ = from_message_id, 608 | limit_ = limit, 609 | filter_ = { 610 | ID = 'SearchMessagesFilter' .. filter 611 | }, 612 | }, cb or dl_cb, cmd) 613 | end 614 | 615 | M.searchChatMessages = searchChatMessages 616 | 617 | -- Searches for messages in all chats except secret chats. Returns result in reverse chronological order, i. e. in order of decreasing (date, chat_id, message_id) 618 | -- @query Query to search for 619 | -- @offset_date Date of the message to search from, you can use 0 or any date in the future to get results from the beginning 620 | -- @offset_chat_id Chat identifier of the last found message or 0 for the first request 621 | -- @offset_message_id Message identifier of the last found message or 0 for the first request 622 | -- @limit Maximum number of messages to be returned, can't be greater than 100 623 | local function searchMessages(query, offset_date, offset_chat_id, offset_message_id, limit, cb, cmd) 624 | if not limit or limit > 100 then 625 | limit = 100 626 | end 627 | 628 | tdcli_function ({ 629 | ID = "SearchMessages", 630 | query_ = query, 631 | offset_date_ = offset_date, 632 | offset_chat_id_ = offset_chat_id, 633 | offset_message_id_ = offset_message_id, 634 | limit_ = limit 635 | }, cb or dl_cb, cmd) 636 | end 637 | 638 | M.searchMessages = searchMessages 639 | 640 | -- Invites bot to a chat (if it is not in the chat) and send /start to it. 641 | -- Bot can't be invited to a private chat other than chat with the bot. 642 | -- Bots can't be invited to broadcast channel chats and secret chats. 643 | -- Returns sent message. 644 | -- UpdateChatTopMessage will not be sent, so returned message should be used to update chat top message 645 | -- @bot_user_id Identifier of the bot 646 | -- @chat_id Identifier of the chat 647 | -- @parameter Hidden parameter sent to bot for deep linking (https://api.telegram.org/bots#deep-linking) 648 | -- parameter=start|startgroup or custom as defined by bot creator 649 | local function sendBotStartMessage(bot_user_id, chat_id, parameter, cb, cmd) 650 | tdcli_function ({ 651 | ID = "SendBotStartMessage", 652 | bot_user_id_ = bot_user_id, 653 | chat_id_ = chat_id, 654 | parameter_ = parameter 655 | }, cb or dl_cb, cmd) 656 | end 657 | 658 | M.sendBotStartMessage = sendBotStartMessage 659 | 660 | -- Sends result of the inline query as a message. 661 | -- Returns sent message. 662 | -- UpdateChatTopMessage will not be sent, so returned message should be used to update chat top message. 663 | -- Always clears chat draft message 664 | -- @chat_id Chat to send message 665 | -- @reply_to_message_id Identifier of a message to reply to or 0 666 | -- @disable_notification Pass true, to disable notification about the message, doesn't works in secret chats 667 | -- @from_background Pass true, if the message is sent from background 668 | -- @query_id Identifier of the inline query 669 | -- @result_id Identifier of the inline result 670 | local function sendInlineQueryResultMessage(chat_id, reply_to_message_id, disable_notification, from_background, query_id, result_id, cb, cmd) 671 | tdcli_function ({ 672 | ID = "SendInlineQueryResultMessage", 673 | chat_id_ = chat_id, 674 | reply_to_message_id_ = reply_to_message_id, 675 | disable_notification_ = disable_notification, 676 | from_background_ = from_background, 677 | query_id_ = query_id, 678 | result_id_ = result_id 679 | }, cb or dl_cb, cmd) 680 | end 681 | 682 | M.sendInlineQueryResultMessage = sendInlineQueryResultMessage 683 | 684 | -- Forwards previously sent messages. 685 | -- Returns forwarded messages in the same order as message identifiers passed in message_ids. 686 | -- If message can't be forwarded, null will be returned instead of the message. 687 | -- UpdateChatTopMessage will not be sent, so returned messages should be used to update chat top message 688 | -- @chat_id Identifier of a chat to forward messages 689 | -- @from_chat_id Identifier of a chat to forward from 690 | -- @message_ids Identifiers of messages to forward 691 | -- @disable_notification Pass true, to disable notification about the message, doesn't works if messages are forwarded to secret chat 692 | -- @from_background Pass true, if the message is sent from background 693 | local function forwardMessages(chat_id, from_chat_id, message_ids, disable_notification, cb, cmd) 694 | tdcli_function ({ 695 | ID = "ForwardMessages", 696 | chat_id_ = chat_id, 697 | from_chat_id_ = from_chat_id, 698 | message_ids_ = message_ids, -- vector 699 | disable_notification_ = disable_notification, 700 | from_background_ = 1 701 | }, cb or dl_cb, cmd) 702 | end 703 | 704 | M.forwardMessages = forwardMessages 705 | 706 | -- Changes current ttl setting in a secret chat and sends corresponding message 707 | -- @chat_id Chat identifier 708 | -- @ttl New value of ttl in seconds 709 | local function sendChatSetTtlMessage(chat_id, ttl, cb, cmd) 710 | tdcli_function ({ 711 | ID = "SendChatSetTtlMessage", 712 | chat_id_ = chat_id, 713 | ttl_ = ttl 714 | }, cb or dl_cb, cmd) 715 | end 716 | 717 | M.sendChatSetTtlMessage = sendChatSetTtlMessage 718 | 719 | -- Deletes messages. 720 | -- UpdateDeleteMessages will not be sent for messages deleted through that function 721 | -- @chat_id Chat identifier 722 | -- @message_ids Identifiers of messages to delete 723 | local function deleteMessages(chat_id, message_ids, cb, cmd) 724 | tdcli_function ({ 725 | ID = "DeleteMessages", 726 | chat_id_ = chat_id, 727 | message_ids_ = message_ids -- vector 728 | }, cb or dl_cb, cmd) 729 | end 730 | 731 | M.deleteMessages = deleteMessages 732 | 733 | -- Deletes all messages in the chat sent by the specified user. 734 | -- Works only in supergroup channel chats, needs appropriate privileges 735 | -- @chat_id Chat identifier 736 | -- @user_id User identifier 737 | local function deleteMessagesFromUser(chat_id, user_id, cb, cmd) 738 | tdcli_function ({ 739 | ID = "DeleteMessagesFromUser", 740 | chat_id_ = chat_id, 741 | user_id_ = user_id 742 | }, cb or dl_cb, cmd) 743 | end 744 | 745 | M.deleteMessagesFromUser = deleteMessagesFromUser 746 | 747 | -- Edits text of text or game message. 748 | -- Non-bots can edit message in a limited period of time. 749 | -- Returns edited message after edit is complete server side 750 | -- @chat_id Chat the message belongs to 751 | -- @message_id Identifier of the message 752 | -- @reply_markup Bots only. New message reply markup 753 | -- @input_message_content New text content of the message. Should be of type InputMessageText 754 | local function editMessageText(chat_id, message_id, reply_markup, text, disable_web_page_preview, parse_mode, cb, cmd) 755 | local TextParseMode = getParseMode(parse_mode) 756 | 757 | tdcli_function ({ 758 | ID = "EditMessageText", 759 | chat_id_ = chat_id, 760 | message_id_ = message_id, 761 | reply_markup_ = reply_markup, -- reply_markup:ReplyMarkup 762 | input_message_content_ = { 763 | ID = "InputMessageText", 764 | text_ = text, 765 | disable_web_page_preview_ = disable_web_page_preview, 766 | clear_draft_ = 0, 767 | entities_ = {}, 768 | parse_mode_ = TextParseMode, 769 | }, 770 | }, cb or dl_cb, cmd) 771 | end 772 | 773 | M.editMessageText = editMessageText 774 | 775 | -- Edits message content caption. 776 | -- Non-bots can edit message in a limited period of time. 777 | -- Returns edited message after edit is complete server side 778 | -- @chat_id Chat the message belongs to 779 | -- @message_id Identifier of the message 780 | -- @reply_markup Bots only. New message reply markup 781 | -- @caption New message content caption, 0-200 characters 782 | local function editMessageCaption(chat_id, message_id, reply_markup, caption, cb, cmd) 783 | tdcli_function ({ 784 | ID = "EditMessageCaption", 785 | chat_id_ = chat_id, 786 | message_id_ = message_id, 787 | reply_markup_ = reply_markup, -- reply_markup:ReplyMarkup 788 | caption_ = caption 789 | }, cb or dl_cb, cmd) 790 | end 791 | 792 | M.editMessageCaption = editMessageCaption 793 | 794 | -- Bots only. 795 | -- Edits message reply markup. 796 | -- Returns edited message after edit is complete server side 797 | -- @chat_id Chat the message belongs to 798 | -- @message_id Identifier of the message 799 | -- @reply_markup New message reply markup 800 | local function editMessageReplyMarkup(inline_message_id, reply_markup, caption, cb, cmd) 801 | tdcli_function ({ 802 | ID = "EditInlineMessageCaption", 803 | inline_message_id_ = inline_message_id, 804 | reply_markup_ = reply_markup, -- reply_markup:ReplyMarkup 805 | caption_ = caption 806 | }, cb or dl_cb, cmd) 807 | end 808 | 809 | M.editMessageReplyMarkup = editMessageReplyMarkup 810 | 811 | -- Bots only. 812 | -- Edits text of an inline text or game message sent via bot 813 | -- @inline_message_id Inline message identifier 814 | -- @reply_markup New message reply markup 815 | -- @input_message_content New text content of the message. Should be of type InputMessageText 816 | local function editInlineMessageText(inline_message_id, reply_markup, text, disable_web_page_preview, cb, cmd) 817 | tdcli_function ({ 818 | ID = "EditInlineMessageText", 819 | inline_message_id_ = inline_message_id, 820 | reply_markup_ = reply_markup, -- reply_markup:ReplyMarkup 821 | input_message_content_ = { 822 | ID = "InputMessageText", 823 | text_ = text, 824 | disable_web_page_preview_ = disable_web_page_preview, 825 | clear_draft_ = 0, 826 | entities_ = {} 827 | }, 828 | }, cb or dl_cb, cmd) 829 | end 830 | 831 | M.editInlineMessageText = editInlineMessageText 832 | 833 | -- Bots only. 834 | -- Edits caption of an inline message content sent via bot 835 | -- @inline_message_id Inline message identifier 836 | -- @reply_markup New message reply markup 837 | -- @caption New message content caption, 0-200 characters 838 | local function editInlineMessageCaption(inline_message_id, reply_markup, caption, cb, cmd) 839 | tdcli_function ({ 840 | ID = "EditInlineMessageCaption", 841 | inline_message_id_ = inline_message_id, 842 | reply_markup_ = reply_markup, -- reply_markup:ReplyMarkup 843 | caption_ = caption 844 | }, cb or dl_cb, cmd) 845 | end 846 | 847 | M.editInlineMessageCaption = editInlineMessageCaption 848 | 849 | -- Bots only. 850 | -- Edits reply markup of an inline message sent via bot 851 | -- @inline_message_id Inline message identifier 852 | -- @reply_markup New message reply markup 853 | local function editInlineMessageReplyMarkup(inline_message_id, reply_markup, cb, cmd) 854 | tdcli_function ({ 855 | ID = "EditInlineMessageReplyMarkup", 856 | inline_message_id_ = inline_message_id, 857 | reply_markup_ = reply_markup -- reply_markup:ReplyMarkup 858 | }, cb or dl_cb, cmd) 859 | end 860 | 861 | M.editInlineMessageReplyMarkup = editInlineMessageReplyMarkup 862 | 863 | 864 | -- Sends inline query to a bot and returns its results. 865 | -- Unavailable for bots 866 | -- @bot_user_id Identifier of the bot send query to 867 | -- @chat_id Identifier of the chat, where the query is sent 868 | -- @user_location User location, only if needed 869 | -- @query Text of the query 870 | -- @offset Offset of the first entry to return 871 | local function getInlineQueryResults(bot_user_id, chat_id, latitude, longitude, query, offset, cb, cmd) 872 | tdcli_function ({ 873 | ID = "GetInlineQueryResults", 874 | bot_user_id_ = bot_user_id, 875 | chat_id_ = chat_id, 876 | user_location_ = { 877 | ID = "Location", 878 | latitude_ = latitude, 879 | longitude_ = longitude 880 | }, 881 | query_ = query, 882 | offset_ = offset 883 | }, cb or dl_cb, cmd) 884 | end 885 | 886 | M.getInlineQueryResults = getInlineQueryResults 887 | 888 | -- Bots only. 889 | -- Sets result of the inline query 890 | -- @inline_query_id Identifier of the inline query 891 | -- @is_personal Does result of the query can be cached only for specified user 892 | -- @results Results of the query 893 | -- @cache_time Allowed time to cache results of the query in seconds 894 | -- @next_offset Offset for the next inline query, pass empty string if there is no more results 895 | -- @switch_pm_text If non-empty, this text should be shown on the button, which opens private chat with the bot and sends bot start message with parameter switch_pm_parameter 896 | -- @switch_pm_parameter Parameter for the bot start message 897 | local function answerInlineQuery(inline_query_id, is_personal, cache_time, next_offset, switch_pm_text, switch_pm_parameter, cb, cmd) 898 | tdcli_function ({ 899 | ID = "AnswerInlineQuery", 900 | inline_query_id_ = inline_query_id, 901 | is_personal_ = is_personal, 902 | results_ = results, --vector, 903 | cache_time_ = cache_time, 904 | next_offset_ = next_offset, 905 | switch_pm_text_ = switch_pm_text, 906 | switch_pm_parameter_ = switch_pm_parameter 907 | }, cb or dl_cb, cmd) 908 | end 909 | 910 | M.answerInlineQuery = answerInlineQuery 911 | 912 | -- Sends callback query to a bot and returns answer to it. 913 | -- Unavailable for bots 914 | -- @chat_id Identifier of the chat with a message 915 | -- @message_id Identifier of the message, from which the query is originated 916 | -- @payload Query payload 917 | -- @text Text of the answer 918 | -- @show_alert If true, an alert should be shown to the user instead of a toast 919 | -- @url URL to be open 920 | local function getCallbackQueryAnswer(chat_id, message_id, text, show_alert, url, cb, cmd) 921 | tdcli_function ({ 922 | ID = "GetCallbackQueryAnswer", 923 | chat_id_ = chat_id, 924 | message_id_ = message_id, 925 | payload_ = { 926 | ID = "CallbackQueryAnswer", 927 | text_ = text, 928 | show_alert_ = show_alert, 929 | url_ = url 930 | }, 931 | }, cb or dl_cb, cmd) 932 | end 933 | 934 | M.getCallbackQueryAnswer = getCallbackQueryAnswer 935 | 936 | -- Bots only. 937 | -- Sets result of the callback query 938 | -- @callback_query_id Identifier of the callback query 939 | -- @text Text of the answer 940 | -- @show_alert If true, an alert should be shown to the user instead of a toast 941 | -- @url Url to be opened 942 | -- @cache_time Allowed time to cache result of the query in seconds 943 | local function answerCallbackQuery(callback_query_id, text, show_alert, url, cache_time, cb, cmd) 944 | tdcli_function ({ 945 | ID = "AnswerCallbackQuery", 946 | callback_query_id_ = callback_query_id, 947 | text_ = text, 948 | show_alert_ = show_alert, 949 | url_ = url, 950 | cache_time_ = cache_time 951 | }, cb or dl_cb, cmd) 952 | end 953 | 954 | M.answerCallbackQuery = answerCallbackQuery 955 | 956 | -- Bots only. 957 | -- Updates game score of the specified user in the game 958 | -- @chat_id Chat a message with the game belongs to 959 | -- @message_id Identifier of the message 960 | -- @edit_message True, if message should be edited 961 | -- @user_id User identifier 962 | -- @score New score 963 | -- @force Pass True to update the score even if it decreases. If score is 0, user will be deleted from the high scores table 964 | local function setGameScore(chat_id, message_id, edit_message, user_id, score, force, cb, cmd) 965 | tdcli_function ({ 966 | ID = "SetGameScore", 967 | chat_id_ = chat_id, 968 | message_id_ = message_id, 969 | edit_message_ = edit_message, 970 | user_id_ = user_id, 971 | score_ = score, 972 | force_ = force 973 | }, cb or dl_cb, cmd) 974 | end 975 | 976 | M.setGameScore = setGameScore 977 | 978 | -- Bots only. 979 | -- Updates game score of the specified user in the game 980 | -- @inline_message_id Inline message identifier 981 | -- @edit_message True, if message should be edited 982 | -- @user_id User identifier 983 | -- @score New score 984 | -- @force Pass True to update the score even if it decreases. If score is 0, user will be deleted from the high scores table 985 | local function setInlineGameScore(inline_message_id, edit_message, user_id, score, force, cb, cmd) 986 | tdcli_function ({ 987 | ID = "SetInlineGameScore", 988 | inline_message_id_ = inline_message_id, 989 | edit_message_ = edit_message, 990 | user_id_ = user_id, 991 | score_ = score, 992 | force_ = force 993 | }, cb or dl_cb, cmd) 994 | end 995 | 996 | M.setInlineGameScore = setInlineGameScore 997 | 998 | -- Bots only. 999 | -- Returns game high scores and some part of the score table around of the specified user in the game 1000 | -- @chat_id Chat a message with the game belongs to 1001 | -- @message_id Identifier of the message 1002 | -- @user_id User identifie 1003 | local function getGameHighScores(chat_id, message_id, user_id, cb, cmd) 1004 | tdcli_function ({ 1005 | ID = "GetGameHighScores", 1006 | chat_id_ = chat_id, 1007 | message_id_ = message_id, 1008 | user_id_ = user_id 1009 | }, cb or dl_cb, cmd) 1010 | end 1011 | 1012 | M.getGameHighScores = getGameHighScores 1013 | 1014 | -- Bots only. 1015 | -- Returns game high scores and some part of the score table around of the specified user in the game 1016 | -- @inline_message_id Inline message identifier 1017 | -- @user_id User identifier 1018 | local function getInlineGameHighScores(inline_message_id, user_id, cb, cmd) 1019 | tdcli_function ({ 1020 | ID = "GetInlineGameHighScores", 1021 | inline_message_id_ = inline_message_id, 1022 | user_id_ = user_id 1023 | }, cb or dl_cb, cmd) 1024 | end 1025 | 1026 | M.getInlineGameHighScores = getInlineGameHighScores 1027 | 1028 | -- Deletes default reply markup from chat. 1029 | -- This method needs to be called after one-time keyboard or ForceReply reply markup has been used. 1030 | -- UpdateChatReplyMarkup will be send if reply markup will be changed 1031 | -- @chat_id Chat identifier 1032 | -- @message_id Message identifier of used keyboard 1033 | local function deleteChatReplyMarkup(chat_id, message_id, cb, cmd) 1034 | tdcli_function ({ 1035 | ID = "DeleteChatReplyMarkup", 1036 | chat_id_ = chat_id, 1037 | message_id_ = message_id 1038 | }, cb or dl_cb, cmd) 1039 | end 1040 | 1041 | M.deleteChatReplyMarkup = deleteChatReplyMarkup 1042 | 1043 | -- Sends notification about user activity in a chat 1044 | -- @chat_id Chat identifier 1045 | -- @action Action description 1046 | -- action = Typing|Cancel|RecordVideo|UploadVideo|RecordVoice|UploadVoice|UploadPhoto|UploadDocument|GeoLocation|ChooseContact|StartPlayGame 1047 | local function sendChatAction(chat_id, action, progress, cb, cmd) 1048 | tdcli_function ({ 1049 | ID = "SendChatAction", 1050 | chat_id_ = chat_id, 1051 | action_ = { 1052 | ID = "SendMessage" .. action .. "Action", 1053 | progress_ = progress or 100 1054 | } 1055 | }, cb or dl_cb, cmd) 1056 | end 1057 | 1058 | M.sendChatAction = sendChatAction 1059 | 1060 | -- Sends notification about screenshot taken in a chat. 1061 | -- Works only in secret chats 1062 | -- @chat_id Chat identifier 1063 | local function sendChatScreenshotTakenNotification(chat_id, cb, cmd) 1064 | tdcli_function ({ 1065 | ID = "SendChatScreenshotTakenNotification", 1066 | chat_id_ = chat_id 1067 | }, cb or dl_cb, cmd) 1068 | end 1069 | 1070 | M.sendChatScreenshotTakenNotification = sendChatScreenshotTakenNotification 1071 | 1072 | -- Chat is opened by the user. 1073 | -- Many useful activities depends on chat being opened or closed. For example, in channels all updates are received only for opened chats 1074 | -- @chat_id Chat identifier 1075 | local function openChat(chat_id, cb, cmd) 1076 | tdcli_function ({ 1077 | ID = "OpenChat", 1078 | chat_id_ = chat_id 1079 | }, cb or dl_cb, cmd) 1080 | end 1081 | 1082 | M.openChat = openChat 1083 | 1084 | -- Chat is closed by the user. 1085 | -- Many useful activities depends on chat being opened or closed. 1086 | -- @chat_id Chat identifier 1087 | local function closeChat(chat_id, cb, cmd) 1088 | tdcli_function ({ 1089 | ID = "CloseChat", 1090 | chat_id_ = chat_id 1091 | }, cb or dl_cb, cmd) 1092 | end 1093 | 1094 | M.closeChat = closeChat 1095 | 1096 | -- Messages are viewed by the user. 1097 | -- Many useful activities depends on message being viewed. For example, marking messages as read, incrementing of view counter, updating of view counter, removing of deleted messages in channels 1098 | -- @chat_id Chat identifier 1099 | -- @message_ids Identifiers of viewed messages 1100 | local function viewMessages(chat_id, message_ids, cb, cmd) 1101 | tdcli_function ({ 1102 | ID = "ViewMessages", 1103 | chat_id_ = chat_id, 1104 | message_ids_ = message_ids -- vector 1105 | }, cb or dl_cb, cmd) 1106 | end 1107 | 1108 | M.viewMessages = viewMessages 1109 | 1110 | -- Message content is opened, for example the user has opened a photo, a video, a document, a location or a venue or have listened to an audio or a voice message 1111 | -- @chat_id Chat identifier of the message 1112 | -- @message_id Identifier of the message with opened content 1113 | local function openMessageContent(chat_id, message_id, cb, cmd) 1114 | tdcli_function ({ 1115 | ID = "OpenMessageContent", 1116 | chat_id_ = chat_id, 1117 | message_id_ = message_id 1118 | }, cb or dl_cb, cmd) 1119 | end 1120 | 1121 | M.openMessageContent = openMessageContent 1122 | 1123 | -- Returns existing chat corresponding to the given user 1124 | -- @user_id User identifier 1125 | local function createPrivateChat(user_id, cb, cmd) 1126 | tdcli_function ({ 1127 | ID = "CreatePrivateChat", 1128 | user_id_ = user_id 1129 | }, cb or dl_cb, cmd) 1130 | end 1131 | 1132 | M.createPrivateChat = createPrivateChat 1133 | 1134 | -- Returns existing chat corresponding to the known group 1135 | -- @group_id Group identifier 1136 | local function createGroupChat(group_id, cb, cmd) 1137 | tdcli_function ({ 1138 | ID = "CreateGroupChat", 1139 | group_id_ = getChatId(group_id).ID 1140 | }, cb or dl_cb, cmd) 1141 | end 1142 | 1143 | M.createGroupChat = createGroupChat 1144 | 1145 | -- Returns existing chat corresponding to the known channel 1146 | -- @channel_id Channel identifier 1147 | local function createChannelChat(channel_id, cb, cmd) 1148 | tdcli_function ({ 1149 | ID = "CreateChannelChat", 1150 | channel_id_ = getChatId(channel_id).ID 1151 | }, cb or dl_cb, cmd) 1152 | end 1153 | 1154 | M.createChannelChat = createChannelChat 1155 | 1156 | -- Returns existing chat corresponding to the known secret chat 1157 | -- @secret_chat_id SecretChat identifier 1158 | local function createSecretChat(secret_chat_id, cb, cmd) 1159 | tdcli_function ({ 1160 | ID = "CreateSecretChat", 1161 | secret_chat_id_ = secret_chat_id 1162 | }, cb or dl_cb, cmd) 1163 | end 1164 | 1165 | M.createSecretChat = createSecretChat 1166 | 1167 | -- Creates new group chat and send corresponding messageGroupChatCreate, returns created chat 1168 | -- @user_ids Identifiers of users to add to the group 1169 | -- @title Title of new group chat, 0-255 characters 1170 | local function createNewGroupChat(user_ids, title, cb, cmd) 1171 | tdcli_function ({ 1172 | ID = "CreateNewGroupChat", 1173 | user_ids_ = user_ids, -- vector 1174 | title_ = title 1175 | }, cb or dl_cb, cmd) 1176 | end 1177 | 1178 | M.createNewGroupChat = createNewGroupChat 1179 | 1180 | -- Creates new channel chat and send corresponding messageChannelChatCreate, returns created chat 1181 | -- @title Title of new channel chat, 0-255 characters 1182 | -- @is_supergroup True, if supergroup chat should be created 1183 | -- @about Information about the channel, 0-255 characters 1184 | local function createNewChannelChat(title, is_supergroup, about, cb, cmd) 1185 | tdcli_function ({ 1186 | ID = "CreateNewChannelChat", 1187 | title_ = title, 1188 | is_supergroup_ = is_supergroup, 1189 | about_ = about 1190 | }, cb or dl_cb, cmd) 1191 | end 1192 | 1193 | M.createNewChannelChat = createNewChannelChat 1194 | 1195 | -- Creates new secret chat, returns created chat 1196 | -- @user_id Identifier of a user to create secret chat with 1197 | local function createNewSecretChat(user_id, cb, cmd) 1198 | tdcli_function ({ 1199 | ID = "CreateNewSecretChat", 1200 | user_id_ = user_id 1201 | }, cb or dl_cb, cmd) 1202 | end 1203 | 1204 | M.createNewSecretChat = createNewSecretChat 1205 | 1206 | -- Creates new channel supergroup chat from existing group chat and send corresponding messageChatMigrateTo and messageChatMigrateFrom. Deactivates group 1207 | -- @chat_id Group chat identifier 1208 | local function migrateGroupChatToChannelChat(chat_id, cb, cmd) 1209 | tdcli_function ({ 1210 | ID = "MigrateGroupChatToChannelChat", 1211 | chat_id_ = chat_id 1212 | }, cb or dl_cb, cmd) 1213 | end 1214 | 1215 | M.migrateGroupChatToChannelChat = migrateGroupChatToChannelChat 1216 | 1217 | -- Changes chat title. 1218 | -- Title can't be changed for private chats. 1219 | -- Title will not change until change will be synchronized with the server. 1220 | -- Title will not be changed if application is killed before it can send request to the server. 1221 | -- There will be update about change of the title on success. Otherwise error will be returned 1222 | -- @chat_id Chat identifier 1223 | -- @title New title of a chat, 0-255 characters 1224 | local function changeChatTitle(chat_id, title, cb, cmd) 1225 | tdcli_function ({ 1226 | ID = "ChangeChatTitle", 1227 | chat_id_ = chat_id, 1228 | title_ = title 1229 | }, cb or dl_cb, cmd) 1230 | end 1231 | 1232 | M.changeChatTitle = changeChatTitle 1233 | 1234 | -- Changes chat photo. 1235 | -- Photo can't be changed for private chats. 1236 | -- Photo will not change until change will be synchronized with the server. 1237 | -- Photo will not be changed if application is killed before it can send request to the server. 1238 | -- There will be update about change of the photo on success. Otherwise error will be returned 1239 | -- @chat_id Chat identifier 1240 | -- @photo New chat photo. You can use zero InputFileId to delete photo. Files accessible only by HTTP URL are not acceptable 1241 | local function changeChatPhoto(chat_id, photo, cb, cmd) 1242 | tdcli_function ({ 1243 | ID = "ChangeChatPhoto", 1244 | chat_id_ = chat_id, 1245 | photo_ = getInputFile(photo) 1246 | }, cb or dl_cb, cmd) 1247 | end 1248 | 1249 | M.changeChatPhoto = changeChatPhoto 1250 | 1251 | -- Changes chat draft message 1252 | -- @chat_id Chat identifier 1253 | -- @draft_message New draft message, nullable 1254 | local function changeChatDraftMessage(chat_id, reply_to_message_id, text, disable_web_page_preview, clear_draft, parse_mode, cb, cmd) 1255 | local TextParseMode = getParseMode(parse_mode) 1256 | 1257 | tdcli_function ({ 1258 | ID = "ChangeChatDraftMessage", 1259 | chat_id_ = chat_id, 1260 | draft_message_ = { 1261 | ID = "DraftMessage", 1262 | reply_to_message_id_ = reply_to_message_id, 1263 | input_message_text_ = { 1264 | ID = "InputMessageText", 1265 | text_ = text, 1266 | disable_web_page_preview_ = disable_web_page_preview, 1267 | clear_draft_ = clear_draft, 1268 | entities_ = {}, 1269 | parse_mode_ = TextParseMode, 1270 | }, 1271 | }, 1272 | }, cb or dl_cb, cmd) 1273 | end 1274 | 1275 | M.changeChatDraftMessage = changeChatDraftMessage 1276 | 1277 | -- Adds new member to chat. 1278 | -- Members can't be added to private or secret chats. 1279 | -- Member will not be added until chat state will be synchronized with the server. 1280 | -- Member will not be added if application is killed before it can send request to the server 1281 | -- @chat_id Chat identifier 1282 | -- @user_id Identifier of the user to add 1283 | -- @forward_limit Number of previous messages from chat to forward to new member, ignored for channel chats 1284 | local function addChatMember(chat_id, user_id, forward_limit, cb, cmd) 1285 | tdcli_function ({ 1286 | ID = "AddChatMember", 1287 | chat_id_ = chat_id, 1288 | user_id_ = user_id, 1289 | forward_limit_ = forward_limit or 50 1290 | }, cb or dl_cb, cmd) 1291 | end 1292 | 1293 | M.addChatMember = addChatMember 1294 | 1295 | -- Adds many new members to the chat. 1296 | -- Currently, available only for channels. 1297 | -- Can't be used to join the channel. 1298 | -- Member will not be added until chat state will be synchronized with the server. 1299 | -- Member will not be added if application is killed before it can send request to the server 1300 | -- @chat_id Chat identifier 1301 | -- @user_ids Identifiers of the users to add 1302 | local function addChatMembers(chat_id, user_ids, cb, cmd) 1303 | tdcli_function ({ 1304 | ID = "AddChatMembers", 1305 | chat_id_ = chat_id, 1306 | user_ids_ = user_ids -- vector 1307 | }, cb or dl_cb, cmd) 1308 | end 1309 | 1310 | M.addChatMembers = addChatMembers 1311 | 1312 | -- Changes status of the chat member, need appropriate privileges. 1313 | -- In channel chats, user will be added to chat members if he is yet not a member and there is less than 200 members in the channel. 1314 | -- Status will not be changed until chat state will be synchronized with the server. 1315 | -- Status will not be changed if application is killed before it can send request to the server 1316 | -- @chat_id Chat identifier 1317 | -- @user_id Identifier of the user to edit status, bots can be editors in the channel chats 1318 | -- @status New status of the member in the chat 1319 | -- status = Creator|Editor|Moderator|Member|Left|Kicked 1320 | local function changeChatMemberStatus(chat_id, user_id, status, cb, cmd) 1321 | tdcli_function ({ 1322 | ID = "ChangeChatMemberStatus", 1323 | chat_id_ = chat_id, 1324 | user_id_ = user_id, 1325 | status_ = { 1326 | ID = "ChatMemberStatus" .. status 1327 | }, 1328 | }, cb or dl_cb, cmd) 1329 | end 1330 | 1331 | M.changeChatMemberStatus = changeChatMemberStatus 1332 | 1333 | -- Returns information about one participant of the chat 1334 | -- @chat_id Chat identifier 1335 | -- @user_id User identifier 1336 | local function getChatMember(chat_id, user_id, cb, cmd) 1337 | tdcli_function ({ 1338 | ID = "GetChatMember", 1339 | chat_id_ = chat_id, 1340 | user_id_ = user_id 1341 | }, cb or dl_cb, cmd) 1342 | end 1343 | 1344 | M.getChatMember = getChatMember 1345 | 1346 | -- Asynchronously downloads file from cloud. 1347 | -- Updates updateFileProgress will notify about download progress. 1348 | -- Update updateFile will notify about successful download 1349 | -- @file_id Identifier of file to download 1350 | local function downloadFile(file_id, cb, cmd) 1351 | tdcli_function ({ 1352 | ID = "DownloadFile", 1353 | file_id_ = file_id 1354 | }, cb or dl_cb, cmd) 1355 | end 1356 | 1357 | M.downloadFile = downloadFile 1358 | 1359 | -- Stops file downloading. 1360 | -- If file already downloaded do nothing. 1361 | -- @file_id Identifier of file to cancel download 1362 | local function cancelDownloadFile(file_id, cb, cmd) 1363 | tdcli_function ({ 1364 | ID = "CancelDownloadFile", 1365 | file_id_ = file_id 1366 | }, cb or dl_cb, cmd) 1367 | end 1368 | 1369 | M.cancelDownloadFile = cancelDownloadFile 1370 | 1371 | -- Next part of a file was generated 1372 | -- @generation_id Identifier of the generation process 1373 | -- @ready Number of bytes already generated. Negative number means that generation has failed and should be terminated 1374 | local function setFileGenerationProgress(generation_id, ready, cb, cmd) 1375 | tdcli_function ({ 1376 | ID = "SetFileGenerationProgress", 1377 | generation_id_ = generation_id, 1378 | ready_ = ready 1379 | }, cb or dl_cb, cmd) 1380 | end 1381 | 1382 | M.setFileGenerationProgress = setFileGenerationProgress 1383 | 1384 | -- Finishes file generation 1385 | -- @generation_id Identifier of the generation process 1386 | local function finishFileGeneration(generation_id, cb, cmd) 1387 | tdcli_function ({ 1388 | ID = "FinishFileGeneration", 1389 | generation_id_ = generation_id 1390 | }, cb or dl_cb, cmd) 1391 | end 1392 | 1393 | M.finishFileGeneration = finishFileGeneration 1394 | 1395 | -- Generates new chat invite link, previously generated link is revoked. 1396 | -- Available for group and channel chats. 1397 | -- Only creator of the chat can export chat invite link 1398 | -- @chat_id Chat identifier 1399 | local function exportChatInviteLink(chat_id, cb, cmd) 1400 | tdcli_function ({ 1401 | ID = "ExportChatInviteLink", 1402 | chat_id_ = chat_id 1403 | }, cb or dl_cb, cmd) 1404 | end 1405 | 1406 | M.exportChatInviteLink = exportChatInviteLink 1407 | 1408 | -- Checks chat invite link for validness and returns information about the corresponding chat 1409 | -- @invite_link Invite link to check. Should begin with "https://telegram.me/joinchat/" 1410 | local function checkChatInviteLink(link, cb, cmd) 1411 | tdcli_function ({ 1412 | ID = "CheckChatInviteLink", 1413 | invite_link_ = link 1414 | }, cb or dl_cb, cmd) 1415 | end 1416 | 1417 | M.checkChatInviteLink = checkChatInviteLink 1418 | 1419 | -- Imports chat invite link, adds current user to a chat if possible. 1420 | -- Member will not be added until chat state will be synchronized with the server. 1421 | -- Member will not be added if application is killed before it can send request to the server 1422 | -- @invite_link Invite link to import. Should begin with "https://telegram.me/joinchat/" 1423 | local function importChatInviteLink(invite_link, cb, cmd) 1424 | tdcli_function ({ 1425 | ID = "ImportChatInviteLink", 1426 | invite_link_ = invite_link 1427 | }, cb or dl_cb, cmd) 1428 | end 1429 | 1430 | M.importChatInviteLink = importChatInviteLink 1431 | 1432 | -- Adds user to black list 1433 | -- @user_id User identifier 1434 | local function blockUser(user_id, cb, cmd) 1435 | tdcli_function ({ 1436 | ID = "BlockUser", 1437 | user_id_ = user_id 1438 | }, cb or dl_cb, cmd) 1439 | end 1440 | 1441 | M.blockUser = blockUser 1442 | 1443 | -- Removes user from black list 1444 | -- @user_id User identifier 1445 | local function unblockUser(user_id, cb, cmd) 1446 | tdcli_function ({ 1447 | ID = "UnblockUser", 1448 | user_id_ = user_id 1449 | }, cb or dl_cb, cmd) 1450 | end 1451 | 1452 | M.unblockUser = unblockUser 1453 | 1454 | -- Returns users blocked by the current user 1455 | -- @offset Number of users to skip in result, must be non-negative 1456 | -- @limit Maximum number of users to return, can't be greater than 100 1457 | local function getBlockedUsers(offset, limit, cb, cmd) 1458 | tdcli_function ({ 1459 | ID = "GetBlockedUsers", 1460 | offset_ = offset, 1461 | limit_ = limit 1462 | }, cb or dl_cb, cmd) 1463 | end 1464 | 1465 | M.getBlockedUsers = getBlockedUsers 1466 | 1467 | -- Adds new contacts/edits existing contacts, contacts user identifiers are ignored. 1468 | -- Returns list of corresponding users in the same order as input contacts. 1469 | -- If contact doesn't registered in Telegram, user with id == 0 will be returned 1470 | -- @contacts List of contacts to import/edit 1471 | local function importContacts(phone_number, first_name, last_name, user_id, cb, cmd) 1472 | tdcli_function ({ 1473 | ID = "ImportContacts", 1474 | contacts_ = {[0] = { 1475 | phone_number_ = tostring(phone_number), 1476 | first_name_ = tostring(first_name), 1477 | last_name_ = tostring(last_name), 1478 | user_id_ = user_id 1479 | }, 1480 | }, 1481 | }, cb or dl_cb, cmd) 1482 | end 1483 | 1484 | M.importContacts = importContacts 1485 | 1486 | -- Searches for specified query in the first name, last name and username of the known user contacts 1487 | -- @query Query to search for, can be empty to return all contacts 1488 | -- @limit Maximum number of users to be returned 1489 | local function searchContacts(query, limit, cb, cmd) 1490 | tdcli_function ({ 1491 | ID = "SearchContacts", 1492 | query_ = query, 1493 | limit_ = limit 1494 | }, cb or dl_cb, cmd) 1495 | end 1496 | 1497 | M.searchContacts = searchContacts 1498 | 1499 | -- Deletes users from contacts list 1500 | -- @user_ids Identifiers of users to be deleted 1501 | local function deleteContacts(user_ids, cb, cmd) 1502 | tdcli_function ({ 1503 | ID = "DeleteContacts", 1504 | user_ids_ = user_ids -- vector 1505 | }, cb or dl_cb, cmd) 1506 | end 1507 | 1508 | M.deleteContacts = deleteContacts 1509 | 1510 | -- Returns profile photos of the user. 1511 | -- Result of this query can't be invalidated, so it must be used with care 1512 | -- @user_id User identifier 1513 | -- @offset Photos to skip, must be non-negative 1514 | -- @limit Maximum number of photos to be returned, can't be greater than 100 1515 | local function getUserProfilePhotos(user_id, offset, limit, cb, cmd) 1516 | tdcli_function ({ 1517 | ID = "GetUserProfilePhotos", 1518 | user_id_ = user_id, 1519 | offset_ = offset, 1520 | limit_ = limit 1521 | }, cb or dl_cb, cmd) 1522 | end 1523 | 1524 | M.getUserProfilePhotos = getUserProfilePhotos 1525 | 1526 | -- Returns stickers corresponding to given emoji 1527 | -- @emoji String representation of emoji. If empty, returns all known stickers 1528 | local function getStickers(emoji, cb, cmd) 1529 | tdcli_function ({ 1530 | ID = "GetStickers", 1531 | emoji_ = emoji 1532 | }, cb or dl_cb, cmd) 1533 | end 1534 | 1535 | M.getStickers = getStickers 1536 | 1537 | -- Returns list of installed sticker sets without archived sticker sets 1538 | -- @is_masks Pass true to return masks, pass false to return stickers 1539 | local function getStickerSets(is_masks, cb, cmd) 1540 | tdcli_function ({ 1541 | ID = "GetStickerSets", 1542 | is_masks_ = is_masks 1543 | }, cb or dl_cb, cmd) 1544 | end 1545 | 1546 | M.getStickerSets = getStickerSets 1547 | 1548 | -- Returns list of archived sticker sets 1549 | -- @is_masks Pass true to return masks, pass false to return stickers 1550 | -- @offset_sticker_set_id Identifier of the sticker set from which return the result 1551 | -- @limit Maximum number of sticker sets to return 1552 | local function getArchivedStickerSets(is_masks, offset_sticker_set_id, limit, cb, cmd) 1553 | tdcli_function ({ 1554 | ID = "GetArchivedStickerSets", 1555 | is_masks_ = is_masks, 1556 | offset_sticker_set_id_ = offset_sticker_set_id, 1557 | limit_ = limit 1558 | }, cb or dl_cb, cmd) 1559 | end 1560 | 1561 | M.getArchivedStickerSets = getArchivedStickerSets 1562 | 1563 | -- Returns list of trending sticker sets 1564 | local function getTrendingStickerSets(dl_cb, cmd) 1565 | tdcli_function ({ 1566 | ID = "GetTrendingStickerSets" 1567 | }, cb or dl_cb, cmd) 1568 | end 1569 | 1570 | M.getTrendingStickerSets = getTrendingStickerSets 1571 | 1572 | -- Returns list of sticker sets attached to a file, currently only photos and videos can have attached sticker sets 1573 | -- @file_id File identifier 1574 | local function getAttachedStickerSets(file_id, cb, cmd) 1575 | tdcli_function ({ 1576 | ID = "GetAttachedStickerSets", 1577 | file_id_ = file_id 1578 | }, cb or dl_cb, cmd) 1579 | end 1580 | 1581 | M.getAttachedStickerSets = getAttachedStickerSets 1582 | 1583 | -- Returns information about sticker set by its identifier 1584 | -- @set_id Identifier of the sticker set 1585 | local function getStickerSet(set_id, cb, cmd) 1586 | tdcli_function ({ 1587 | ID = "GetStickerSet", 1588 | set_id_ = set_id 1589 | }, cb or dl_cb, cmd) 1590 | end 1591 | 1592 | M.getStickerSet = getStickerSet 1593 | 1594 | -- Searches sticker set by its short name 1595 | -- @name Name of the sticker set 1596 | local function searchStickerSet(name, cb, cmd) 1597 | tdcli_function ({ 1598 | ID = "SearchStickerSet", 1599 | name_ = name 1600 | }, cb or dl_cb, cmd) 1601 | end 1602 | 1603 | M.searchStickerSet = searchStickerSet 1604 | 1605 | -- Installs/uninstalls or enables/archives sticker set. 1606 | -- Official sticker set can't be uninstalled, but it can be archived 1607 | -- @set_id Identifier of the sticker set 1608 | -- @is_installed New value of is_installed 1609 | -- @is_archived New value of is_archived 1610 | local function updateStickerSet(set_id, is_installed, is_archived, cb, cmd) 1611 | tdcli_function ({ 1612 | ID = "UpdateStickerSet", 1613 | set_id_ = set_id, 1614 | is_installed_ = is_installed, 1615 | is_archived_ = is_archived 1616 | }, cb or dl_cb, cmd) 1617 | end 1618 | 1619 | M.updateStickerSet = updateStickerSet 1620 | 1621 | -- Trending sticker sets are viewed by the user 1622 | -- @sticker_set_ids Identifiers of viewed trending sticker sets 1623 | local function viewTrendingStickerSets(sticker_set_ids, cb, cmd) 1624 | tdcli_function ({ 1625 | ID = "ViewTrendingStickerSets", 1626 | sticker_set_ids_ = sticker_set_ids -- vector 1627 | }, cb or dl_cb, cmd) 1628 | end 1629 | 1630 | M.viewTrendingStickerSets = viewTrendingStickerSets 1631 | 1632 | -- Changes the order of installed sticker sets 1633 | -- @is_masks Pass true to change masks order, pass false to change stickers order 1634 | -- @sticker_set_ids Identifiers of installed sticker sets in the new right order 1635 | local function reorderStickerSets(is_masks, sticker_set_ids, cb, cmd) 1636 | tdcli_function ({ 1637 | ID = "ReorderStickerSets", 1638 | is_masks_ = is_masks, 1639 | sticker_set_ids_ = sticker_set_ids -- vector 1640 | }, cb or dl_cb, cmd) 1641 | end 1642 | 1643 | M.reorderStickerSets = reorderStickerSets 1644 | 1645 | -- Returns list of recently used stickers 1646 | -- @is_attached Pass true to return stickers and masks recently attached to photo or video files, pass false to return recently sent stickers 1647 | local function getRecentStickers(is_attached, cb, cmd) 1648 | tdcli_function ({ 1649 | ID = "GetRecentStickers", 1650 | is_attached_ = is_attached 1651 | }, cb or dl_cb, cmd) 1652 | end 1653 | 1654 | M.getRecentStickers = getRecentStickers 1655 | 1656 | -- Manually adds new sticker to the list of recently used stickers. 1657 | -- New sticker is added to the beginning of the list. 1658 | -- If the sticker is already in the list, at first it is removed from the list 1659 | -- @is_attached Pass true to add the sticker to the list of stickers recently attached to photo or video files, pass false to add the sticker to the list of recently sent stickers 1660 | -- @sticker Sticker file to add 1661 | local function addRecentSticker(is_attached, sticker, cb, cmd) 1662 | tdcli_function ({ 1663 | ID = "AddRecentSticker", 1664 | is_attached_ = is_attached, 1665 | sticker_ = getInputFile(sticker) 1666 | }, cb or dl_cb, cmd) 1667 | end 1668 | 1669 | M.addRecentSticker = addRecentSticker 1670 | 1671 | -- Removes a sticker from the list of recently used stickers 1672 | -- @is_attached Pass true to remove the sticker from the list of stickers recently attached to photo or video files, pass false to remove the sticker from the list of recently sent stickers 1673 | -- @sticker Sticker file to delete 1674 | local function deleteRecentSticker(is_attached, sticker, cb, cmd) 1675 | tdcli_function ({ 1676 | ID = "DeleteRecentSticker", 1677 | is_attached_ = is_attached, 1678 | sticker_ = getInputFile(sticker) 1679 | }, cb or dl_cb, cmd) 1680 | end 1681 | 1682 | M.deleteRecentSticker = deleteRecentSticker 1683 | 1684 | -- Clears list of recently used stickers 1685 | -- @is_attached Pass true to clear list of stickers recently attached to photo or video files, pass false to clear the list of recently sent stickers 1686 | local function clearRecentStickers(is_attached, cb, cmd) 1687 | tdcli_function ({ 1688 | ID = "ClearRecentStickers", 1689 | is_attached_ = is_attached 1690 | }, cb or dl_cb, cmd) 1691 | end 1692 | 1693 | M.clearRecentStickers = clearRecentStickers 1694 | 1695 | -- Returns emojis corresponding to a sticker 1696 | -- @sticker Sticker file identifier 1697 | local function getStickerEmojis(sticker, cb, cmd) 1698 | tdcli_function ({ 1699 | ID = "GetStickerEmojis", 1700 | sticker_ = getInputFile(sticker) 1701 | }, cb or dl_cb, cmd) 1702 | end 1703 | 1704 | M.getStickerEmojis = getStickerEmojis 1705 | 1706 | -- Returns saved animations 1707 | local function getSavedAnimations(dl_cb, cmd) 1708 | tdcli_function ({ 1709 | ID = "GetSavedAnimations", 1710 | }, cb or dl_cb, cmd) 1711 | end 1712 | 1713 | M.getSavedAnimations = getSavedAnimations 1714 | 1715 | -- Manually adds new animation to the list of saved animations. 1716 | -- New animation is added to the beginning of the list. 1717 | -- If the animation is already in the list, at first it is removed from the list. 1718 | -- Only non-secret video animations with MIME type "video/mp4" can be added to the list 1719 | -- @animation Animation file to add. Only known to server animations (i. e. successfully sent via message) can be added to the list 1720 | local function addSavedAnimation(animation, cb, cmd) 1721 | tdcli_function ({ 1722 | ID = "AddSavedAnimation", 1723 | animation_ = getInputFile(animation) 1724 | }, cb or dl_cb, cmd) 1725 | end 1726 | 1727 | M.addSavedAnimation = addSavedAnimation 1728 | 1729 | -- Removes animation from the list of saved animations 1730 | -- @animation Animation file to delete 1731 | local function deleteSavedAnimation(animation, cb, cmd) 1732 | tdcli_function ({ 1733 | ID = "DeleteSavedAnimation", 1734 | animation_ = getInputFile(animation) 1735 | }, cb or dl_cb, cmd) 1736 | end 1737 | 1738 | M.deleteSavedAnimation = deleteSavedAnimation 1739 | 1740 | -- Returns up to 20 recently used inline bots in the order of the last usage 1741 | local function getRecentInlineBots(dl_cb, cmd) 1742 | tdcli_function ({ 1743 | ID = "GetRecentInlineBots", 1744 | }, cb or dl_cb, cmd) 1745 | end 1746 | 1747 | M.getRecentInlineBots = getRecentInlineBots 1748 | 1749 | -- Get web page preview by text of the message. 1750 | -- Do not call this function to often 1751 | -- @message_text Message text 1752 | local function getWebPagePreview(message_text, cb, cmd) 1753 | tdcli_function ({ 1754 | ID = "GetWebPagePreview", 1755 | message_text_ = message_text 1756 | }, cb or dl_cb, cmd) 1757 | end 1758 | 1759 | M.getWebPagePreview = getWebPagePreview 1760 | 1761 | -- Returns notification settings for a given scope 1762 | -- @scope Scope to return information about notification settings 1763 | -- scope = Chat(chat_id)|PrivateChats|GroupChats|AllChats| 1764 | local function getNotificationSettings(scope, chat_id, cb, cmd) 1765 | tdcli_function ({ 1766 | ID = "GetNotificationSettings", 1767 | scope_ = { 1768 | ID = 'NotificationSettingsFor' .. scope, 1769 | chat_id_ = chat_id or nil 1770 | }, 1771 | }, cb or dl_cb, cmd) 1772 | end 1773 | 1774 | M.getNotificationSettings = getNotificationSettings 1775 | 1776 | -- Changes notification settings for a given scope 1777 | -- @scope Scope to change notification settings 1778 | -- @notification_settings New notification settings for given scope 1779 | -- scope = Chat(chat_id)|PrivateChats|GroupChats|AllChats| 1780 | local function setNotificationSettings(scope, chat_id, mute_for, show_preview, cb, cmd) 1781 | tdcli_function ({ 1782 | ID = "SetNotificationSettings", 1783 | scope_ = { 1784 | ID = 'NotificationSettingsFor' .. scope, 1785 | chat_id_ = chat_id or nil 1786 | }, 1787 | notification_settings_ = { 1788 | ID = "NotificationSettings", 1789 | mute_for_ = mute_for, 1790 | sound_ = "default", 1791 | show_preview_ = show_preview 1792 | } 1793 | }, cb or dl_cb, cmd) 1794 | end 1795 | 1796 | M.setNotificationSettings = setNotificationSettings 1797 | 1798 | -- Resets all notification settings to the default value. 1799 | -- By default the only muted chats are supergroups, sound is set to 'default' and message previews are showed 1800 | local function resetAllNotificationSettings(dl_cb, cmd) 1801 | tdcli_function ({ 1802 | ID = "ResetAllNotificationSettings" 1803 | }, cb or dl_cb, cmd) 1804 | end 1805 | 1806 | M.resetAllNotificationSettings = resetAllNotificationSettings 1807 | 1808 | -- Uploads new profile photo for logged in user. 1809 | -- Photo will not change until change will be synchronized with the server. 1810 | -- Photo will not be changed if application is killed before it can send request to the server. 1811 | -- If something changes, updateUser will be sent 1812 | -- @photo_path Path to new profile photo 1813 | local function setProfilePhoto(photo_path, cb, cmd) 1814 | tdcli_function ({ 1815 | ID = "SetProfilePhoto", 1816 | photo_path_ = photo_path 1817 | }, cb or dl_cb, cmd) 1818 | end 1819 | 1820 | M.setProfilePhoto = setProfilePhoto 1821 | 1822 | -- Deletes profile photo. 1823 | -- If something changes, updateUser will be sent 1824 | -- @profile_photo_id Identifier of profile photo to delete 1825 | local function deleteProfilePhoto(profile_photo_id, cb, cmd) 1826 | tdcli_function ({ 1827 | ID = "DeleteProfilePhoto", 1828 | profile_photo_id_ = profile_photo_id 1829 | }, cb or dl_cb, cmd) 1830 | end 1831 | 1832 | M.deleteProfilePhoto = deleteProfilePhoto 1833 | 1834 | -- Changes first and last names of logged in user. 1835 | -- If something changes, updateUser will be sent 1836 | -- @first_name New value of user first name, 1-255 characters 1837 | -- @last_name New value of optional user last name, 0-255 characters 1838 | local function changeName(first_name, last_name, cb, cmd) 1839 | tdcli_function ({ 1840 | ID = "ChangeName", 1841 | first_name_ = first_name, 1842 | last_name_ = last_name 1843 | }, cb or dl_cb, cmd) 1844 | end 1845 | 1846 | M.changeName = changeName 1847 | 1848 | -- Changes about information of logged in user 1849 | -- @about New value of userFull.about, 0-255 characters 1850 | local function changeAbout(about, cb, cmd) 1851 | tdcli_function ({ 1852 | ID = "ChangeAbout", 1853 | about_ = about 1854 | }, cb or dl_cb, cmd) 1855 | end 1856 | 1857 | M.changeAbout = changeAbout 1858 | 1859 | -- Changes username of logged in user. 1860 | -- If something changes, updateUser will be sent 1861 | -- @username New value of username. Use empty string to remove username 1862 | local function changeUsername(username, cb, cmd) 1863 | tdcli_function ({ 1864 | ID = "ChangeUsername", 1865 | username_ = username 1866 | }, cb or dl_cb, cmd) 1867 | end 1868 | 1869 | M.changeUsername = changeUsername 1870 | 1871 | -- Changes user's phone number and sends authentication code to the new user's phone number. 1872 | -- Returns authStateWaitCode with information about sent code on success 1873 | -- @phone_number New user's phone number in any reasonable format 1874 | -- @allow_flash_call Pass True, if code can be sent via flash call to the specified phone number 1875 | -- @is_current_phone_number Pass true, if the phone number is used on the current device. Ignored if allow_flash_call is False 1876 | local function changePhoneNumber(phone_number, allow_flash_call, is_current_phone_number, cb, cmd) 1877 | tdcli_function ({ 1878 | ID = "ChangePhoneNumber", 1879 | phone_number_ = phone_number, 1880 | allow_flash_call_ = allow_flash_call, 1881 | is_current_phone_number_ = is_current_phone_number 1882 | }, cb or dl_cb, cmd) 1883 | end 1884 | 1885 | M.changePhoneNumber = changePhoneNumber 1886 | 1887 | -- Resends authentication code sent to change user's phone number. 1888 | -- Works only if in previously received authStateWaitCode next_code_type was not null. 1889 | -- Returns authStateWaitCode on success 1890 | local function resendChangePhoneNumberCode(dl_cb, cmd) 1891 | tdcli_function ({ 1892 | ID = "ResendChangePhoneNumberCode", 1893 | }, cb or dl_cb, cmd) 1894 | end 1895 | 1896 | M.resendChangePhoneNumberCode = resendChangePhoneNumberCode 1897 | 1898 | -- Checks authentication code sent to change user's phone number. 1899 | -- Returns authStateOk on success 1900 | -- @code Verification code from SMS, voice call or flash call 1901 | local function checkChangePhoneNumberCode(code, cb, cmd) 1902 | tdcli_function ({ 1903 | ID = "CheckChangePhoneNumberCode", 1904 | code_ = code 1905 | }, cb or dl_cb, cmd) 1906 | end 1907 | 1908 | M.checkChangePhoneNumberCode = checkChangePhoneNumberCode 1909 | 1910 | -- Returns all active sessions of logged in user 1911 | local function getActiveSessions(dl_cb, cmd) 1912 | tdcli_function ({ 1913 | ID = "GetActiveSessions", 1914 | }, cb or dl_cb, cmd) 1915 | end 1916 | 1917 | M.getActiveSessions = getActiveSessions 1918 | 1919 | -- Terminates another session of logged in user 1920 | -- @session_id Session identifier 1921 | local function terminateSession(session_id, cb, cmd) 1922 | tdcli_function ({ 1923 | ID = "TerminateSession", 1924 | session_id_ = session_id 1925 | }, cb or dl_cb, cmd) 1926 | end 1927 | 1928 | M.terminateSession = terminateSession 1929 | 1930 | -- Terminates all other sessions of logged in user 1931 | local function terminateAllOtherSessions(dl_cb, cmd) 1932 | tdcli_function ({ 1933 | ID = "TerminateAllOtherSessions", 1934 | }, cb or dl_cb, cmd) 1935 | end 1936 | 1937 | M.terminateAllOtherSessions = terminateAllOtherSessions 1938 | 1939 | -- Gives or revokes all members of the group editor rights. 1940 | -- Needs creator privileges in the group 1941 | -- @group_id Identifier of the group 1942 | -- @anyone_can_edit New value of anyone_can_edit 1943 | local function toggleGroupEditors(group_id, anyone_can_edit, cb, cmd) 1944 | tdcli_function ({ 1945 | ID = "ToggleGroupEditors", 1946 | group_id_ = getChatId(group_id).ID, 1947 | anyone_can_edit_ = anyone_can_edit 1948 | }, cb or dl_cb, cmd) 1949 | end 1950 | 1951 | M.toggleGroupEditors = toggleGroupEditors 1952 | 1953 | -- Changes username of the channel. 1954 | -- Needs creator privileges in the channel 1955 | -- @channel_id Identifier of the channel 1956 | -- @username New value of username. Use empty string to remove username 1957 | local function changeChannelUsername(channel_id, username, cb, cmd) 1958 | tdcli_function ({ 1959 | ID = "ChangeChannelUsername", 1960 | channel_id_ = getChatId(channel_id).ID, 1961 | username_ = username 1962 | }, cb or dl_cb, cmd) 1963 | end 1964 | 1965 | M.changeChannelUsername = changeChannelUsername 1966 | 1967 | -- Gives or revokes right to invite new members to all current members of the channel. 1968 | -- Needs creator privileges in the channel. 1969 | -- Available only for supergroups 1970 | -- @channel_id Identifier of the channel 1971 | -- @anyone_can_invite New value of anyone_can_invite 1972 | local function toggleChannelInvites(channel_id, anyone_can_invite, cb, cmd) 1973 | tdcli_function ({ 1974 | ID = "ToggleChannelInvites", 1975 | channel_id_ = getChatId(channel_id).ID, 1976 | anyone_can_invite_ = anyone_can_invite 1977 | }, cb or dl_cb, cmd) 1978 | end 1979 | 1980 | M.toggleChannelInvites = toggleChannelInvites 1981 | 1982 | -- Enables or disables sender signature on sent messages in the channel. 1983 | -- Needs creator privileges in the channel. 1984 | -- Not available for supergroups 1985 | -- @channel_id Identifier of the channel 1986 | -- @sign_messages New value of sign_messages 1987 | local function toggleChannelSignMessages(channel_id, sign_messages, cb, cmd) 1988 | tdcli_function ({ 1989 | ID = "ToggleChannelSignMessages", 1990 | channel_id_ = getChatId(channel_id).ID, 1991 | sign_messages_ = sign_messages 1992 | }, cb or dl_cb, cmd) 1993 | end 1994 | 1995 | M.toggleChannelSignMessages = toggleChannelSignMessages 1996 | 1997 | -- Changes information about the channel. 1998 | -- Needs creator privileges in the broadcast channel or editor privileges in the supergroup channel 1999 | -- @channel_id Identifier of the channel 2000 | -- @about New value of about, 0-255 characters 2001 | local function changeChannelAbout(channel_id, about, cb, cmd) 2002 | tdcli_function ({ 2003 | ID = "ChangeChannelAbout", 2004 | channel_id_ = getChatId(channel_id).ID, 2005 | about_ = about 2006 | }, cb or dl_cb, cmd) 2007 | end 2008 | 2009 | M.changeChannelAbout = changeChannelAbout 2010 | 2011 | -- Pins a message in a supergroup channel chat. 2012 | -- Needs editor privileges in the channel 2013 | -- @channel_id Identifier of the channel 2014 | -- @message_id Identifier of the new pinned message 2015 | -- @disable_notification True, if there should be no notification about the pinned message 2016 | local function pinChannelMessage(channel_id, message_id, disable_notification, cb, cmd) 2017 | tdcli_function ({ 2018 | ID = "PinChannelMessage", 2019 | channel_id_ = getChatId(channel_id).ID, 2020 | message_id_ = message_id, 2021 | disable_notification_ = disable_notification 2022 | }, cb or dl_cb, cmd) 2023 | end 2024 | 2025 | M.pinChannelMessage = pinChannelMessage 2026 | 2027 | -- Removes pinned message in the supergroup channel. 2028 | -- Needs editor privileges in the channel 2029 | -- @channel_id Identifier of the channel 2030 | local function unpinChannelMessage(channel_id, cb, cmd) 2031 | tdcli_function ({ 2032 | ID = "UnpinChannelMessage", 2033 | channel_id_ = getChatId(channel_id).ID 2034 | }, cb or dl_cb, cmd) 2035 | end 2036 | 2037 | M.unpinChannelMessage = unpinChannelMessage 2038 | 2039 | -- Reports some supergroup channel messages from a user as spam messages 2040 | -- @channel_id Channel identifier 2041 | -- @user_id User identifier 2042 | -- @message_ids Identifiers of messages sent in the supergroup by the user, the list should be non-empty 2043 | local function reportChannelSpam(channel_id, user_id, message_ids, cb, cmd) 2044 | tdcli_function ({ 2045 | ID = "ReportChannelSpam", 2046 | channel_id_ = getChatId(channel_id).ID, 2047 | user_id_ = user_id, 2048 | message_ids_ = message_ids -- vector 2049 | }, cb or dl_cb, cmd) 2050 | end 2051 | 2052 | M.reportChannelSpam = reportChannelSpam 2053 | 2054 | -- Returns information about channel members or kicked from channel users. 2055 | -- Can be used only if channel_full->can_get_members == true 2056 | -- @channel_id Identifier of the channel 2057 | -- @filter Kind of channel users to return, defaults to channelMembersRecent 2058 | -- @offset Number of channel users to skip 2059 | -- @limit Maximum number of users be returned, can't be greater than 200 2060 | -- filter = Recent|Administrators|Kicked|Bots 2061 | local function getChannelMembers(channel_id, offset, filter, limit, cb, cmd) 2062 | if not limit or limit > 200 then 2063 | limit = 200 2064 | end 2065 | 2066 | tdcli_function ({ 2067 | ID = "GetChannelMembers", 2068 | channel_id_ = getChatId(channel_id).ID, 2069 | filter_ = { 2070 | ID = "ChannelMembers" .. filter 2071 | }, 2072 | offset_ = offset, 2073 | limit_ = limit 2074 | }, cb or dl_cb, cmd) 2075 | end 2076 | 2077 | M.getChannelMembers = getChannelMembers 2078 | 2079 | -- Deletes channel along with all messages in corresponding chat. 2080 | -- Releases channel username and removes all members. 2081 | -- Needs creator privileges in the channel. 2082 | -- Channels with more than 1000 members can't be deleted 2083 | -- @channel_id Identifier of the channel 2084 | local function deleteChannel(channel_id, cb, cmd) 2085 | tdcli_function ({ 2086 | ID = "DeleteChannel", 2087 | channel_id_ = getChatId(channel_id).ID 2088 | }, cb or dl_cb, cmd) 2089 | end 2090 | 2091 | M.deleteChannel = deleteChannel 2092 | 2093 | -- Returns list of created public channels 2094 | local function getCreatedPublicChannels(dl_cb, cmd) 2095 | tdcli_function ({ 2096 | ID = "GetCreatedPublicChannels" 2097 | }, cb or dl_cb, cmd) 2098 | end 2099 | 2100 | M.getCreatedPublicChannels = getCreatedPublicChannels 2101 | 2102 | -- Closes secret chat 2103 | -- @secret_chat_id Secret chat identifier 2104 | local function closeSecretChat(secret_chat_id, cb, cmd) 2105 | tdcli_function ({ 2106 | ID = "CloseSecretChat", 2107 | secret_chat_id_ = secret_chat_id 2108 | }, cb or dl_cb, cmd) 2109 | end 2110 | 2111 | M.closeSecretChat = closeSecretChat 2112 | 2113 | -- Returns user that can be contacted to get support 2114 | local function getSupportUser(dl_cb, cmd) 2115 | tdcli_function ({ 2116 | ID = "GetSupportUser", 2117 | }, cb or dl_cb, cmd) 2118 | end 2119 | 2120 | M.getSupportUser = getSupportUser 2121 | 2122 | -- Returns background wallpapers 2123 | local function getWallpapers(dl_cb, cmd) 2124 | tdcli_function ({ 2125 | ID = "GetWallpapers", 2126 | }, cb or dl_cb, cmd) 2127 | end 2128 | 2129 | M.getWallpapers = getWallpapers 2130 | 2131 | -- Registers current used device for receiving push notifications 2132 | -- @device_token Device token 2133 | -- device_token = apns|gcm|mpns|simplePush|ubuntuPhone|blackberry 2134 | local function registerDevice(device_token, token, device_token_set, cb, cmd) 2135 | local dToken = {ID = device_token .. 'DeviceToken', token_ = token} 2136 | 2137 | if device_token_set then 2138 | dToken = {ID = "DeviceTokenSet", token_ = device_token_set} -- tokens:vector 2139 | end 2140 | 2141 | tdcli_function ({ 2142 | ID = "RegisterDevice", 2143 | device_token_ = dToken 2144 | }, cb or dl_cb, cmd) 2145 | end 2146 | 2147 | M.registerDevice = registerDevice 2148 | 2149 | -- Returns list of used device tokens 2150 | local function getDeviceTokens(dl_cb, cmd) 2151 | tdcli_function ({ 2152 | ID = "GetDeviceTokens", 2153 | }, cb or dl_cb, cmd) 2154 | end 2155 | 2156 | M.getDeviceTokens = getDeviceTokens 2157 | 2158 | -- Changes privacy settings 2159 | -- @key Privacy key 2160 | -- @rules New privacy rules 2161 | -- @privacyKeyUserStatus Privacy key for managing visibility of the user status 2162 | -- @privacyKeyChatInvite Privacy key for managing ability of invitation of the user to chats 2163 | -- @privacyRuleAllowAll Rule to allow all users 2164 | -- @privacyRuleAllowContacts Rule to allow all user contacts 2165 | -- @privacyRuleAllowUsers Rule to allow specified users 2166 | -- @user_ids User identifiers 2167 | -- @privacyRuleDisallowAll Rule to disallow all users 2168 | -- @privacyRuleDisallowContacts Rule to disallow all user contacts 2169 | -- @privacyRuleDisallowUsers Rule to disallow all specified users 2170 | -- key = UserStatus|ChatInvite 2171 | -- rules = AllowAll|AllowContacts|AllowUsers(user_ids)|DisallowAll|DisallowContacts|DisallowUsers(user_ids) 2172 | local function setPrivacy(key, rule, allowed_user_ids, disallowed_user_ids, cb, cmd) 2173 | local rules = {[0] = {ID = 'PrivacyRule' .. rule}} 2174 | 2175 | if allowed_user_ids then 2176 | rules = { 2177 | { 2178 | ID = 'PrivacyRule' .. rule 2179 | }, 2180 | [0] = { 2181 | ID = "PrivacyRuleAllowUsers", 2182 | user_ids_ = allowed_user_ids -- vector 2183 | }, 2184 | } 2185 | end 2186 | if disallowed_user_ids then 2187 | rules = { 2188 | { 2189 | ID = 'PrivacyRule' .. rule 2190 | }, 2191 | [0] = { 2192 | ID = "PrivacyRuleDisallowUsers", 2193 | user_ids_ = disallowed_user_ids -- vector 2194 | }, 2195 | } 2196 | end 2197 | if allowed_user_ids and disallowed_user_ids then 2198 | rules = { 2199 | { 2200 | ID = 'PrivacyRule' .. rule 2201 | }, 2202 | { 2203 | ID = "PrivacyRuleAllowUsers", 2204 | user_ids_ = allowed_user_ids 2205 | }, 2206 | [0] = { 2207 | ID = "PrivacyRuleDisallowUsers", 2208 | user_ids_ = disallowed_user_ids 2209 | }, 2210 | } 2211 | end 2212 | tdcli_function ({ 2213 | ID = "SetPrivacy", 2214 | key_ = { 2215 | ID = 'PrivacyKey' .. key 2216 | }, 2217 | rules_ = { 2218 | ID = "PrivacyRules", 2219 | rules_ = rules 2220 | }, 2221 | }, cb or dl_cb, cmd) 2222 | end 2223 | 2224 | M.setPrivacy = setPrivacy 2225 | 2226 | -- Returns current privacy settings 2227 | -- @key Privacy key 2228 | -- key = UserStatus|ChatInvite 2229 | local function getPrivacy(key, cb, cmd) 2230 | tdcli_function ({ 2231 | ID = "GetPrivacy", 2232 | key_ = { 2233 | ID = "PrivacyKey" .. key 2234 | }, 2235 | }, cb or dl_cb, cmd) 2236 | end 2237 | 2238 | M.getPrivacy = getPrivacy 2239 | 2240 | -- Returns value of an option by its name. 2241 | -- See list of available options on https://core.telegram.org/tdlib/options 2242 | -- @name Name of the option 2243 | local function getOption(name, cb, cmd) 2244 | tdcli_function ({ 2245 | ID = "GetOption", 2246 | name_ = name 2247 | }, cb or dl_cb, cmd) 2248 | end 2249 | 2250 | M.getOption = getOption 2251 | 2252 | -- Sets value of an option. 2253 | -- See list of available options on https://core.telegram.org/tdlib/options. 2254 | -- Only writable options can be set 2255 | -- @name Name of the option 2256 | -- @value New value of the option 2257 | local function setOption(name, option, value, cb, cmd) 2258 | tdcli_function ({ 2259 | ID = "SetOption", 2260 | name_ = name, 2261 | value_ = { 2262 | ID = 'Option' .. option, 2263 | value_ = value 2264 | }, 2265 | }, cb or dl_cb, cmd) 2266 | end 2267 | 2268 | M.setOption = setOption 2269 | 2270 | -- Changes period of inactivity, after which the account of currently logged in user will be automatically deleted 2271 | -- @ttl New account TTL 2272 | local function changeAccountTtl(days, cb, cmd) 2273 | tdcli_function ({ 2274 | ID = "ChangeAccountTtl", 2275 | ttl_ = { 2276 | ID = "AccountTtl", 2277 | days_ = days 2278 | }, 2279 | }, cb or dl_cb, cmd) 2280 | end 2281 | 2282 | M.changeAccountTtl = changeAccountTtl 2283 | 2284 | -- Returns period of inactivity, after which the account of currently logged in user will be automatically deleted 2285 | local function getAccountTtl(dl_cb, cmd) 2286 | tdcli_function ({ 2287 | ID = "GetAccountTtl", 2288 | }, cb or dl_cb, cmd) 2289 | end 2290 | 2291 | M.getAccountTtl = getAccountTtl 2292 | 2293 | -- Deletes the account of currently logged in user, deleting from the server all information associated with it. 2294 | -- Account's phone number can be used to create new account, but only once in two weeks 2295 | -- @reason Optional reason of account deletion 2296 | local function deleteAccount(reason, cb, cmd) 2297 | tdcli_function ({ 2298 | ID = "DeleteAccount", 2299 | reason_ = reason 2300 | }, cb or dl_cb, cmd) 2301 | end 2302 | 2303 | M.deleteAccount = deleteAccount 2304 | 2305 | -- Returns current chat report spam state 2306 | -- @chat_id Chat identifier 2307 | local function getChatReportSpamState(chat_id, cb, cmd) 2308 | tdcli_function ({ 2309 | ID = "GetChatReportSpamState", 2310 | chat_id_ = chat_id 2311 | }, cb or dl_cb, cmd) 2312 | end 2313 | 2314 | M.getChatReportSpamState = getChatReportSpamState 2315 | 2316 | -- Reports chat as a spam chat or as not a spam chat. 2317 | -- Can be used only if ChatReportSpamState.can_report_spam is true. 2318 | -- After this request ChatReportSpamState.can_report_spam became false forever 2319 | -- @chat_id Chat identifier 2320 | -- @is_spam_chat If true, chat will be reported as a spam chat, otherwise it will be marked as not a spam chat 2321 | local function changeChatReportSpamState(chat_id, is_spam_chat, cb, cmd) 2322 | tdcli_function ({ 2323 | ID = "ChangeChatReportSpamState", 2324 | chat_id_ = chat_id, 2325 | is_spam_chat_ = is_spam_chat 2326 | }, cb or dl_cb, cmd) 2327 | end 2328 | 2329 | M.changeChatReportSpamState = changeChatReportSpamState 2330 | 2331 | -- Bots only. 2332 | -- Informs server about number of pending bot updates if they aren't processed for a long time 2333 | -- @pending_update_count Number of pending updates 2334 | -- @error_message Last error's message 2335 | local function setBotUpdatesStatus(pending_update_count, error_message, cb, cmd) 2336 | tdcli_function ({ 2337 | ID = "SetBotUpdatesStatus", 2338 | pending_update_count_ = pending_update_count, 2339 | error_message_ = error_message 2340 | }, cb or dl_cb, cmd) 2341 | end 2342 | 2343 | M.setBotUpdatesStatus = setBotUpdatesStatus 2344 | 2345 | -- Returns Ok after specified amount of the time passed 2346 | -- @seconds Number of seconds before that function returns 2347 | local function setAlarm(seconds, cb, cmd) 2348 | tdcli_function ({ 2349 | ID = "SetAlarm", 2350 | seconds_ = seconds 2351 | }, cb or dl_cb, cmd) 2352 | end 2353 | 2354 | M.setAlarm = setAlarm 2355 | 2356 | -- Text message 2357 | -- @text Text to send 2358 | -- @disable_notification Pass true, to disable notification about the message, doesn't works in secret chats 2359 | -- @from_background Pass true, if the message is sent from background 2360 | -- @reply_markup Bots only. Markup for replying to message 2361 | -- @disable_web_page_preview Pass true to disable rich preview for link in the message text 2362 | -- @clear_draft Pass true if chat draft message should be deleted 2363 | -- @entities Bold, Italic, Code, Pre, PreCode and TextUrl entities contained in the text. Non-bot users can't use TextUrl entities. Can't be used with non-null parse_mode 2364 | -- @parse_mode Text parse mode, nullable. Can't be used along with enitities 2365 | --[[local function sendText(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, text, disable_web_page_preview, parse_mode, cb, cmd) 2366 | local TextParseMode = getParseMode(parse_mode) 2367 | local input_message_content = { 2368 | ID = "InputMessageText", 2369 | text_ = text, 2370 | disable_web_page_preview_ = disable_web_page_preview, 2371 | clear_draft_ = 0, 2372 | entities_ = {}, 2373 | parse_mode_ = TextParseMode, 2374 | } 2375 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2376 | end 2377 | 2378 | M.sendText = sendText 2379 | ]] 2380 | local function sendMessage(chat_id, reply_to_message_id, disable_notification, text, disable_web_page_preview, parse_mode) 2381 | local TextParseMode = getParseMode(parse_mode) 2382 | local tt = db:get('endmsg') or '' 2383 | tdcli_function ({ 2384 | ID = "SendMessage", 2385 | chat_id_ = chat_id, 2386 | reply_to_message_id_ = reply_to_message_id, 2387 | disable_notification_ = disable_notification, 2388 | from_background_ = 1, 2389 | reply_markup_ = nil, 2390 | input_message_content_ = { 2391 | ID = "InputMessageText", 2392 | text_ = text..'\n\n'..tt, 2393 | disable_web_page_preview_ = disable_web_page_preview, 2394 | clear_draft_ = 0, 2395 | entities_ = {}, 2396 | parse_mode_ = TextParseMode, 2397 | }, 2398 | }, dl_cb, extra) 2399 | end 2400 | 2401 | M.sendMessage = sendMessage 2402 | -- Animation message 2403 | -- @animation Animation file to send 2404 | -- @thumb Animation thumb, if available 2405 | -- @width Width of the animation, may be replaced by the server 2406 | -- @height Height of the animation, may be replaced by the server 2407 | -- @caption Animation caption, 0-200 characters 2408 | local function sendAnimation(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, animation, width, height, caption, cb, cmd) 2409 | local input_message_content = { 2410 | ID = "InputMessageAnimation", 2411 | animation_ = getInputFile(animation), 2412 | width_ = 0, 2413 | height_ = 0, 2414 | caption_ = caption 2415 | } 2416 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2417 | end 2418 | 2419 | M.sendAnimation = sendAnimation 2420 | 2421 | -- Audio message 2422 | -- @audio Audio file to send 2423 | -- @album_cover_thumb Thumb of the album's cover, if available 2424 | -- @duration Duration of audio in seconds, may be replaced by the server 2425 | -- @title Title of the audio, 0-64 characters, may be replaced by the server 2426 | -- @performer Performer of the audio, 0-64 characters, may be replaced by the server 2427 | -- @caption Audio caption, 0-200 characters 2428 | local function sendAudio(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, audio, duration, title, performer, caption, cb, cmd) 2429 | local input_message_content = { 2430 | ID = "InputMessageAudio", 2431 | audio_ = getInputFile(audio), 2432 | duration_ = duration or 0, 2433 | title_ = title or 0, 2434 | performer_ = performer, 2435 | caption_ = caption 2436 | } 2437 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2438 | end 2439 | 2440 | M.sendAudio = sendAudio 2441 | 2442 | -- Document message 2443 | -- @document Document to send 2444 | -- @thumb Document thumb, if available 2445 | -- @caption Document caption, 0-200 characters 2446 | local function sendDocument(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, document, caption, cb, cmd) 2447 | local input_message_content = { 2448 | ID = "InputMessageDocument", 2449 | document_ = getInputFile(document), 2450 | caption_ = caption 2451 | } 2452 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2453 | end 2454 | 2455 | M.sendDocument = sendDocument 2456 | 2457 | -- Photo message 2458 | -- @photo Photo to send 2459 | -- @caption Photo caption, 0-200 characters 2460 | local function sendPhoto(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, photo, caption, cb, cmd) 2461 | local input_message_content = { 2462 | ID = "InputMessagePhoto", 2463 | photo_ = getInputFile(photo), 2464 | added_sticker_file_ids_ = {}, 2465 | width_ = 0, 2466 | height_ = 0, 2467 | caption_ = caption 2468 | } 2469 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2470 | end 2471 | 2472 | M.sendPhoto = sendPhoto 2473 | 2474 | -- Sticker message 2475 | -- @sticker Sticker to send 2476 | -- @thumb Sticker thumb, if available 2477 | local function sendSticker(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, sticker, cb, cmd) 2478 | local input_message_content = { 2479 | ID = "InputMessageSticker", 2480 | sticker_ = getInputFile(sticker), 2481 | width_ = 0, 2482 | height_ = 0 2483 | } 2484 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2485 | end 2486 | 2487 | M.sendSticker = sendSticker 2488 | 2489 | -- Video message 2490 | -- @video Video to send 2491 | -- @thumb Video thumb, if available 2492 | -- @duration Duration of video in seconds 2493 | -- @width Video width 2494 | -- @height Video height 2495 | -- @caption Video caption, 0-200 characters 2496 | local function sendVideo(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, video, duration, width, height, caption, cb, cmd) 2497 | local input_message_content = { 2498 | ID = "InputMessageVideo", 2499 | video_ = getInputFile(video), 2500 | added_sticker_file_ids_ = {}, 2501 | duration_ = duration or 0, 2502 | width_ = width or 0, 2503 | height_ = height or 0, 2504 | caption_ = caption 2505 | } 2506 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2507 | end 2508 | 2509 | M.sendVideo = sendVideo 2510 | 2511 | -- Voice message 2512 | -- @voice Voice file to send 2513 | -- @duration Duration of voice in seconds 2514 | -- @waveform Waveform representation of the voice in 5-bit format 2515 | -- @caption Voice caption, 0-200 characters 2516 | local function sendVoice(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, voice, duration, waveform, caption, cb, cmd) 2517 | local input_message_content = { 2518 | ID = "InputMessageVoice", 2519 | voice_ = getInputFile(voice), 2520 | duration_ = duration or 0, 2521 | waveform_ = waveform, 2522 | caption_ = caption 2523 | } 2524 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2525 | end 2526 | 2527 | M.sendVoice = sendVoice 2528 | 2529 | -- Message with location 2530 | -- @latitude Latitude of location in degrees as defined by sender 2531 | -- @longitude Longitude of location in degrees as defined by sender 2532 | local function sendLocation(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, latitude, longitude, cb, cmd) 2533 | local input_message_content = { 2534 | ID = "InputMessageLocation", 2535 | location_ = { 2536 | ID = "Location", 2537 | latitude_ = latitude, 2538 | longitude_ = longitude 2539 | }, 2540 | } 2541 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2542 | end 2543 | 2544 | M.sendLocation = sendLocation 2545 | 2546 | -- Message with information about venue 2547 | -- @venue Venue to send 2548 | -- @latitude Latitude of location in degrees as defined by sender 2549 | -- @longitude Longitude of location in degrees as defined by sender 2550 | -- @title Venue name as defined by sender 2551 | -- @address Venue address as defined by sender 2552 | -- @provider Provider of venue database as defined by sender. Only "foursquare" need to be supported currently 2553 | -- @id Identifier of the venue in provider database as defined by sender 2554 | local function sendVenue(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, latitude, longitude, title, address, id, cb, cmd) 2555 | local input_message_content = { 2556 | ID = "InputMessageVenue", 2557 | venue_ = { 2558 | ID = "Venue", 2559 | location_ = { 2560 | ID = "Location", 2561 | latitude_ = latitude, 2562 | longitude_ = longitude 2563 | }, 2564 | title_ = title, 2565 | address_ = address, 2566 | provider_ = 'foursquare', 2567 | id_ = id 2568 | }, 2569 | } 2570 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2571 | end 2572 | 2573 | M.sendVenue = sendVenue 2574 | 2575 | -- User contact message 2576 | -- @contact Contact to send 2577 | -- @phone_number User's phone number 2578 | -- @first_name User first name, 1-255 characters 2579 | -- @last_name User last name 2580 | -- @user_id User identifier if known, 0 otherwise 2581 | local function sendContact(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, phone_number, first_name, last_name, user_id, cb, cmd) 2582 | local input_message_content = { 2583 | ID = "InputMessageContact", 2584 | contact_ = { 2585 | ID = "Contact", 2586 | phone_number_ = phone_number, 2587 | first_name_ = first_name, 2588 | last_name_ = last_name, 2589 | user_id_ = user_id 2590 | }, 2591 | } 2592 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2593 | end 2594 | 2595 | M.sendContact = sendContact 2596 | 2597 | -- Message with a game 2598 | -- @bot_user_id User identifier of a bot owned the game 2599 | -- @game_short_name Game short name 2600 | local function sendGame(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, bot_user_id, game_short_name, cb, cmd) 2601 | local input_message_content = { 2602 | ID = "InputMessageGame", 2603 | bot_user_id_ = bot_user_id, 2604 | game_short_name_ = game_short_name 2605 | } 2606 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2607 | end 2608 | 2609 | M.sendGame = sendGame 2610 | 2611 | -- Forwarded message 2612 | -- @from_chat_id Chat identifier of the message to forward 2613 | -- @message_id Identifier of the message to forward 2614 | local function sendForwarded(chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, from_chat_id, message_id, cb, cmd) 2615 | local input_message_content = { 2616 | ID = "InputMessageForwarded", 2617 | from_chat_id_ = from_chat_id, 2618 | message_id_ = message_id, 2619 | in_game_share_ = in_game_share 2620 | } 2621 | sendRequest('SendMessage', chat_id, reply_to_message_id, disable_notification, from_background, reply_markup, input_message_content, cb, cmd) 2622 | end 2623 | 2624 | M.sendForwarded = sendForwarded 2625 | 2626 | return M 2627 | --------------------------------------------------------------------------------