├── data ├── BDManager └── photos │ └── BDManager ├── download_path └── BDManager ├── autobd.sh ├── libs ├── redis.lua ├── serpent.lua └── dkjson.lua ├── README.md ├── beyond.sh ├── plugins ├── plugins.lua ├── msg_checks.lua ├── fun.lua ├── tools.lua └── banhammer.lua ├── bot ├── bot.lua ├── methods.lua └── utils.lua └── LICENSE /data/BDManager: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/photos/BDManager: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /download_path/BDManager: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /autobd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | COUNTER=1 3 | while(true) do 4 | ./beyond.sh 5 | let COUNTER=COUNTER+1 6 | done 7 | -------------------------------------------------------------------------------- /libs/redis.lua: -------------------------------------------------------------------------------- 1 | local Redis = (loadfile "./libs/lua-redis.lua")() 2 | local FakeRedis = (loadfile "./libs/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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [BDManager v2.0](https://t.me/BeyondTeam) 2 | An advanced administration bot based on BDMessenger 3 | 4 | [![Bot API](http://img.shields.io/badge/Bot%20API-v3.4-00aced.svg)](https://core.telegram.org/bots/api) 5 | [![https://t.me/BeyondTeam](https://img.shields.io/badge/💬%20Telegram-BeyondTeam-00aced.svg)](https://t.me/BeyondTeam) 6 | 7 | * * * 8 | 9 | ## Configure 10 | 11 | * Put Your Bot `TOKEN` At Line `3` in bot.lua 12 | * Put Your `Telegram ID` At Line `6 & 189` in bot.lua 13 | * Put Your `Channel ID` At Line `5` in bot.lua 14 | 15 | # Installation 16 | 17 | ```sh 18 | # Let's install the bot. 19 | cd $HOME 20 | git clone https://github.com/BeyondTeam/BDManager.git 21 | cd BDManager 22 | chmod +x beyond.sh 23 | ./beyond.sh install 24 | ./beyond.sh 25 | 26 | 27 | # For Auto Launch: 28 | chmod 777 autobd.sh 29 | tmux 30 | ./autobd.sh 31 | # End ;) 32 | ``` 33 | ### One command 34 | To install everything in one command, use: 35 | ```sh 36 | cd $HOME && git clone https://github.com/BeyondTeam/BDManager.git && cd BDManager && chmod +x beyond.sh && ./beyond.sh install && ./beyond.sh 37 | ``` 38 | 39 | 40 | # Support and Development 41 | 42 | More information [Beyond Global Chat](https://t.me/joinchat/AAAAAEGaKOxC8K6cJ3bCcw) 43 | 44 | # Special thanks to 45 | 46 | `Beyond Team Members` 47 | 48 | * * * 49 | 50 | # Developers! 51 | 52 | [SoLiD](https://github.com/solid021) ([Telegram](https://t.me/SoLiD)) 53 | 54 | [MAKAN](https://github.com/makanj) ([Telegram](https://t.me/MAKAN)) 55 | 56 | [ToOfan](https://github.com/To0fan) ([Telegram](https://t.me/ToOfan)) 57 | 58 | [TheNIS](https://t.me/bypa3r) 59 | 60 | ### Our Telegram Channel: 61 | 62 | [@BeyondTeam](https://t.me/BeyondTeam) 63 | 64 | ### Our Forum: 65 | 66 | [Beyond Team Forum](https://Beyond-Dev.ir) 67 | -------------------------------------------------------------------------------- /beyond.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | THIS_DIR=$(cd $(dirname $0); pwd) 4 | cd $THIS_DIR 5 | 6 | install() { 7 | sudo apt-get update -y 8 | sudo apt-get upgrade -y 9 | sudo apt-get install lua5.1 lua-socket lua-sec redis-server curl -y 10 | sudo apt-get install libreadline-dev libssl-dev lua5.2 luarocks liblua5.2-dev curl libcurl4-gnutls-dev -y 11 | git clone http://github.com/keplerproject/luarocks 12 | cd luarocks 13 | ./configure --lua-version=5.2 14 | make build 15 | sudo make install 16 | sudo luarocks install Lua-cURL 17 | sudo luarocks install oauth 18 | sudo luarocks install redis-lua 19 | sudo luarocks install lua-cjson 20 | sudo luarocks install ansicolors 21 | sudo luarocks install serpent 22 | cd .. 23 | } 24 | 25 | function print_logo() { 26 | green " ____ ____ _____" 27 | green " | _ )| _ \ |_ _|___ ____ __ __" 28 | green " | _ \| |_) ) | |/ .__| _ \_| \/ |" 29 | green " |____/|____/ |_|\____/\_____|_/\/\_|" 30 | echo -e "\n\e[0m" 31 | } 32 | 33 | function logo_play() { 34 | declare -A txtlogo 35 | seconds="0.010" 36 | txtlogo[1]=" ____ ____ _____" 37 | txtlogo[2]="| _ )| _ \ |_ _|___ ____ __ __" 38 | txtlogo[3]="| _ \| |_) ) | |/ .__| _ \_| \/ |" 39 | txtlogo[4]="|____/|____/ |_|\____/\_____|_/\/\_|" 40 | printf "\e[31m\t" 41 | for i in ${!txtlogo[@]}; do 42 | for x in `seq 0 ${#txtlogo[$i]}`; do 43 | printf "${txtlogo[$i]:$x:1}" 44 | sleep $seconds 45 | done 46 | printf "\n\t" 47 | done 48 | printf "\n" 49 | echo -e "\e[0m" 50 | } 51 | 52 | function beyondteam() { 53 | echo -e "\e[0m" 54 | green " >>>> We Are Not Attacker " 55 | green " >>>> We Are Not Alliance " 56 | white " >>>> We Are Programmer " 57 | white " >>>> We Are The Best " 58 | red " >>>> We Are Family " 59 | red " >>>> @BeyondTeam " 60 | echo -e "\e[0m" 61 | } 62 | 63 | red() { 64 | printf '\e[1;31m%s\n\e[0;39;49m' "$@" 65 | } 66 | green() { 67 | printf '\e[1;32m%s\n\e[0;39;49m' "$@" 68 | } 69 | white() { 70 | printf '\e[1;37m%s\n\e[0;39;49m' "$@" 71 | } 72 | update() { 73 | git pull 74 | } 75 | 76 | if [ "$1" = "install" ]; then 77 | print_logo 78 | beyondteam 79 | logo_play 80 | install 81 | elif [ "$1" = "update" ]; then 82 | logo_play 83 | beyondteam 84 | update 85 | exit 1 86 | else 87 | print_logo 88 | beyondteam 89 | logo_play 90 | green "Beyond Manager Bot running..." 91 | lua ./bot/bot.lua 92 | fi 93 | -------------------------------------------------------------------------------- /plugins/plugins.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | -- Returns the key (index) in the config.enabled_plugins table 4 | local function plugin_enabled( name ) 5 | for k,v in pairs(_config.enabled_plugins) do 6 | if name == v then 7 | return k 8 | end 9 | end 10 | -- If not found 11 | return false 12 | end 13 | 14 | -- Returns true if file exists in plugins folder 15 | local function plugin_exists( name ) 16 | for k,v in pairs(plugins_names()) do 17 | if name..'.lua' == v then 18 | return true 19 | end 20 | end 21 | return false 22 | end 23 | 24 | local function list_all_plugins(only_enabled) 25 | local tmp = '\n\n[BeyondTeam](Telegram.Me/BeyondTeam)' 26 | local text = '' 27 | local nsum = 0 28 | for k, v in pairs( plugins_names( )) do 29 | -- ✔ enabled, ❌ disabled 30 | local status = '*|✖️|>*' 31 | nsum = nsum+1 32 | nact = 0 33 | -- Check if is enabled 34 | for k2, v2 in pairs(_config.enabled_plugins) do 35 | if v == v2..'.lua' then 36 | status = '*|✔|>*' 37 | end 38 | nact = nact+1 39 | end 40 | if not only_enabled or status == '*|✔|>*'then 41 | -- get the name 42 | v = string.match (v, "(.*)%.lua") 43 | text = text..nsum..'.'..status..' '..check_markdown(v)..' \n' 44 | end 45 | end 46 | local text = text..'\n\n'..nsum..' *📂plugins installed*\n\n'..nact..' _✔️plugins enabled_\n\n'..nsum-nact..' _❌plugins disabled_\n\n[BeyondTeam](Telegram.Me/BeyondTeam)' 47 | return text 48 | end 49 | 50 | local function list_plugins(only_enabled) 51 | local text = '' 52 | local nsum = 0 53 | for k, v in pairs( plugins_names( )) do 54 | -- ✔ enabled, ❌ disabled 55 | local status = '*|✖️|>*' 56 | nsum = nsum+1 57 | nact = 0 58 | -- Check if is enabled 59 | for k2, v2 in pairs(_config.enabled_plugins) do 60 | if v == v2..'.lua' then 61 | status = '*|✔|>*' 62 | end 63 | nact = nact+1 64 | end 65 | if not only_enabled or status == '*|✔|>*'then 66 | -- get the name 67 | v = string.match (v, "(.*)%.lua") 68 | -- text = text..v..' '..status..'\n' 69 | end 70 | end 71 | local text = text.."\n_🔃All Plugins Reloaded_\n\n"..nact.." *✔️Plugins Enabled*\n"..nsum.." *📂Plugins Installed*\n\n[BeyondTeam](Telegram.Me/BeyondTeam)" 72 | return text 73 | end 74 | 75 | local function reload_plugins( ) 76 | bot_run() 77 | plugins = {} 78 | load_plugins() 79 | return list_plugins(true) 80 | end 81 | 82 | 83 | local function enable_plugin( plugin_name ) 84 | print('checking if '..plugin_name..' exists') 85 | -- Check if plugin is enabled 86 | if plugin_enabled(plugin_name) then 87 | return ''..plugin_name..' _is enabled_' 88 | end 89 | -- Checks if plugin exists 90 | if plugin_exists(plugin_name) then 91 | -- Add to the config table 92 | table.insert(_config.enabled_plugins, plugin_name) 93 | print(plugin_name..' added to _config table') 94 | save_config() 95 | -- Reload the plugins 96 | return reload_plugins( ) 97 | else 98 | return ''..plugin_name..' _does not exists_' 99 | end 100 | end 101 | 102 | local function disable_plugin( name, chat ) 103 | -- Check if plugins exists 104 | if not plugin_exists(name) then 105 | return ' '..name..' _does not exists_' 106 | end 107 | local k = plugin_enabled(name) 108 | -- Check if plugin is enabled 109 | if not k then 110 | return ' '..name..' _not enabled_' 111 | end 112 | -- Disable and reload 113 | table.remove(_config.enabled_plugins, k) 114 | save_config( ) 115 | return reload_plugins(true) 116 | end 117 | 118 | local function disable_plugin_on_chat(receiver, plugin) 119 | if not plugin_exists(plugin) then 120 | return "_Plugin doesn't exists_" 121 | end 122 | 123 | if not _config.disabled_plugin_on_chat then 124 | _config.disabled_plugin_on_chat = {} 125 | end 126 | 127 | if not _config.disabled_plugin_on_chat[receiver] then 128 | _config.disabled_plugin_on_chat[receiver] = {} 129 | end 130 | 131 | _config.disabled_plugin_on_chat[receiver][plugin] = true 132 | 133 | save_config() 134 | return ' '..plugin..' _disabled on this chat_' 135 | end 136 | 137 | local function reenable_plugin_on_chat(receiver, plugin) 138 | if not _config.disabled_plugin_on_chat then 139 | return 'There aren\'t any disabled plugins' 140 | end 141 | 142 | if not _config.disabled_plugin_on_chat[receiver] then 143 | return 'There aren\'t any disabled plugins for this chat' 144 | end 145 | 146 | if not _config.disabled_plugin_on_chat[receiver][plugin] then 147 | return '_This plugin is not disabled_' 148 | end 149 | 150 | _config.disabled_plugin_on_chat[receiver][plugin] = false 151 | save_config() 152 | return ' '..plugin..' is enabled again' 153 | end 154 | 155 | local function run(msg, matches) 156 | -- Show the available plugins 157 | if is_sudo(msg) then 158 | if matches[1]:lower() == 'plist' then --after changed to moderator mode, set only sudo 159 | return list_all_plugins() 160 | end 161 | end 162 | -- Re-enable a plugin for this chat 163 | if matches[1] == 'pl' then 164 | if matches[2] == '+' and matches[4] == 'chat' then 165 | if is_momod(msg) then 166 | local receiver = msg.to.id 167 | local plugin = matches[3] 168 | print("enable "..plugin..' on this chat') 169 | return reenable_plugin_on_chat(receiver, plugin) 170 | end 171 | end 172 | 173 | -- Enable a plugin 174 | if matches[2] == '+' and is_sudo(msg) then --after changed to moderator mode, set only sudo 175 | if is_mod(msg) then 176 | local plugin_name = matches[3] 177 | print("enable: "..matches[3]) 178 | return enable_plugin(plugin_name) 179 | end 180 | end 181 | -- Disable a plugin on a chat 182 | if matches[2] == '-' and matches[4] == 'chat' then 183 | if is_mod(msg) then 184 | local plugin = matches[3] 185 | local receiver = msg.to.id 186 | print("disable "..plugin..' on this chat') 187 | return disable_plugin_on_chat(receiver, plugin) 188 | end 189 | end 190 | -- Disable a plugin 191 | if matches[2] == '-' and is_sudo(msg) then --after changed to moderator mode, set only sudo 192 | if matches[3] == 'plugins' then 193 | return 'This plugin can\'t be disabled' 194 | end 195 | print("disable: "..matches[3]) 196 | return disable_plugin(matches[3]) 197 | end 198 | if matches[2] == '*' and is_sudo(msg) then --after changed to moderator mode, set only sudo 199 | return reload_plugins(true) 200 | end 201 | end 202 | -- Reload all the plugins! 203 | if matches[1]:lower() == 'reload' and is_sudo(msg) then --after changed to moderator mode, set only sudo 204 | return reload_plugins(true) 205 | end 206 | end 207 | 208 | return { 209 | description = "Plugin to manage other plugins. Enable, disable or reload.", 210 | usage = { 211 | moderator = { 212 | "!plug disable [plugin] chat : disable plugin only this chat.", 213 | "!plug enable [plugin] chat : enable plugin only this chat.", 214 | }, 215 | sudo = { 216 | "!plist : list all plugins.", 217 | "!pl + [plugin] : enable plugin.", 218 | "!pl - [plugin] : disable plugin.", 219 | "!pl * : reloads all plugins." }, 220 | }, 221 | patterns = { 222 | "^[!/](plist)$", 223 | "^[!/](pl) (+) ([%w_%.%-]+)$", 224 | "^[!/](pl) (-) ([%w_%.%-]+)$", 225 | "^[!/](pl) (+) ([%w_%.%-]+) (chat)", 226 | "^[!/](pl) (-) ([%w_%.%-]+) (chat)", 227 | "^[!/](pl) (*)$", 228 | "^[!/](reload)$" 229 | }, 230 | run = run 231 | } 232 | 233 | end 234 | 235 | -------------------------------------------------------------------------------- /libs/serpent.lua: -------------------------------------------------------------------------------- 1 | local n, v = "serpent", 0.28 -- (C) 2012-15 Paul Kulchenko; MIT License 2 | local c, d = "Paul Kulchenko", "Lua serializer and pretty printer" 3 | local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'} 4 | local badtype = {thread = true, userdata = true, cdata = true} 5 | local keyword, globals, G = {}, {}, (_G or _ENV) 6 | for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 7 | 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', 8 | 'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end 9 | for k,v in pairs(G) do globals[v] = k end -- build func to name mapping 10 | for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'}) do 11 | for k,v in pairs(G[g] or {}) do globals[v] = g..'.'..k end end 12 | 13 | local function s(t, opts) 14 | local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum 15 | local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge 16 | local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge) 17 | local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge) 18 | local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0 19 | local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)", 20 | -- tostring(val) is needed because __tostring may return a non-string value 21 | function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return tostring(syms[s]) end)) end 22 | local function safestr(s) return type(s) == "number" and tostring(huge and snum[tostring(s)] or s) 23 | or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026 24 | or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end 25 | local function comment(s,l) return comm and (l or 0) < comm and ' --[['..tostring(s)..']]' or '' end 26 | local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal 27 | and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end 28 | local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r'] 29 | local n = name == nil and '' or name 30 | local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n] 31 | local safe = plain and n or '['..safestr(n)..']' 32 | return (path or '')..(plain and path and '.' or '')..safe, safe end 33 | local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding 34 | local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'} 35 | local function padnum(d) return ("%0"..tostring(maxn).."d"):format(tonumber(d)) end 36 | table.sort(k, function(a,b) 37 | -- sort numeric keys first: k[key] is not nil for numerical keys 38 | return (k[a] ~= nil and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum)) 39 | < (k[b] ~= nil and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end 40 | local function val2str(t, name, indent, insref, path, plainindex, level) 41 | local ttype, level, mt = type(t), (level or 0), getmetatable(t) 42 | local spath, sname = safename(path, name) 43 | local tag = plainindex and 44 | ((type(name) == "number") and '' or name..space..'='..space) or 45 | (name ~= nil and sname..space..'='..space or '') 46 | if seen[t] then -- already seen this element 47 | sref[#sref+1] = spath..space..'='..space..seen[t] 48 | return tag..'nil'..comment('ref', level) end 49 | if type(mt) == 'table' and (mt.__serialize or mt.__tostring) then -- knows how to serialize itself 50 | seen[t] = insref or spath 51 | if mt.__serialize then t = mt.__serialize(t) else t = tostring(t) end 52 | ttype = type(t) end -- new value falls through to be serialized 53 | if ttype == "table" then 54 | if level >= maxl then return tag..'{}'..comment('max', level) end 55 | seen[t] = insref or spath 56 | if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty 57 | local maxn, o, out = math.min(#t, maxnum or #t), {}, {} 58 | for key = 1, maxn do o[key] = key end 59 | if not maxnum or #o < maxnum then 60 | local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables 61 | for key in pairs(t) do if o[key] ~= key then n = n + 1; o[n] = key end end end 62 | if maxnum and #o > maxnum then o[maxnum+1] = nil end 63 | if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end 64 | local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output) 65 | for n, key in ipairs(o) do 66 | local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse 67 | if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing 68 | or opts.keyallow and not opts.keyallow[key] 69 | or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types 70 | or sparse and value == nil then -- skipping nils; do nothing 71 | elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then 72 | if not seen[key] and not globals[key] then 73 | sref[#sref+1] = 'placeholder' 74 | local sname = safename(iname, gensym(key)) -- iname is table for local variables 75 | sref[#sref] = val2str(key,sname,indent,sname,iname,true) end 76 | sref[#sref+1] = 'placeholder' 77 | local path = seen[t]..'['..tostring(seen[key] or globals[key] or gensym(key))..']' 78 | sref[#sref] = path..space..'='..space..tostring(seen[value] or val2str(value,nil,indent,path)) 79 | else 80 | out[#out+1] = val2str(value,key,indent,insref,seen[t],plainindex,level+1) 81 | end 82 | end 83 | local prefix = string.rep(indent or '', level) 84 | local head = indent and '{\n'..prefix..indent or '{' 85 | local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space)) 86 | local tail = indent and "\n"..prefix..'}' or '}' 87 | return (custom and custom(tag,head,body,tail) or tag..head..body..tail)..comment(t, level) 88 | elseif badtype[ttype] then 89 | seen[t] = insref or spath 90 | return tag..globerr(t, level) 91 | elseif ttype == 'function' then 92 | seen[t] = insref or spath 93 | local ok, res = pcall(string.dump, t) 94 | local func = ok and ((opts.nocode and "function() --[[..skipped..]] end" or 95 | "((loadstring or load)("..safestr(res)..",'@serialized'))")..comment(t, level)) 96 | return tag..(func or globerr(t, level)) 97 | else return tag..safestr(t) end -- handle all other types 98 | end 99 | local sepr = indent and "\n" or ";"..space 100 | local body = val2str(t, name, indent) -- this call also populates sref 101 | local tail = #sref>1 and table.concat(sref, sepr)..sepr or '' 102 | local warn = opts.comment and #sref>1 and space.."--[[incomplete output with shared/self-references skipped]]" or '' 103 | return not name and body..warn or "do local "..body..sepr..tail.."return "..name..sepr.."end" 104 | end 105 | 106 | local function deserialize(data, opts) 107 | local env = (opts and opts.safe == false) and G 108 | or setmetatable({}, { 109 | __index = function(t,k) return t end, 110 | __call = function(t,...) error("cannot call functions") end 111 | }) 112 | local f, res = (loadstring or load)('return '..data, nil, nil, env) 113 | if not f then f, res = (loadstring or load)(data, nil, nil, env) end 114 | if not f then return f, res end 115 | if setfenv then setfenv(f, env) end 116 | return pcall(f) 117 | end 118 | 119 | local function merge(a, b) if b then for k,v in pairs(b) do a[k] = v end end; return a; end 120 | return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s, 121 | load = deserialize, 122 | dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end, 123 | line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end, 124 | block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end } 125 | -------------------------------------------------------------------------------- /bot/bot.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path..';.luarocks/share/lua/5.2/?.lua;.luarocks/share/lua/5.2/?/init.lua' 2 | package.cpath = package.cpath..';.luarocks/lib/lua/5.2/?.so' 3 | bot_token = "Token" 4 | send_api = "https://api.telegram.org/bot"..bot_token 5 | BeyondTeam = -1001011351482 6 | sudo_id = 157059515 7 | http = require('socket.http') 8 | https = require('ssl.https') 9 | URL = require('socket.url') 10 | curl = require('cURL') 11 | ltn12 = require("ltn12") 12 | cUrl_Command = curl.easy{verbose = true} 13 | redis = (loadfile "./libs/redis.lua")() 14 | json = (loadfile "./libs/JSON.lua")() 15 | JSON = (loadfile "./libs/dkjson.lua")() 16 | serpent = (loadfile "./libs/serpent.lua")() 17 | require('./bot/methods') 18 | require('./bot/utils') 19 | -- @BeyondTeam 20 | function bot_run() 21 | bot = nil 22 | while not bot do 23 | bot = send_req(send_api.."/getMe") 24 | end 25 | bot = bot.result 26 | local runlog = bot.first_name.." [@"..bot.username.."]\nis run in: "..os.date("%F - %H:%M:%S") 27 | print(runlog) 28 | send_msg(sudo_id, runlog) 29 | last_update = last_update or 0 30 | last_cron = last_cron or os.time() 31 | startbot = true 32 | our_id = bot.id 33 | end 34 | 35 | function send_req(url) 36 | local dat, res = https.request(url) 37 | local tab = JSON.decode(dat) 38 | if res ~= 200 then return false end 39 | if not tab.ok then return false end 40 | return tab 41 | end 42 | 43 | function bot_updates(offset) 44 | local url = send_api.."/getUpdates?timeout=10" 45 | if offset then 46 | url = url.."&offset="..offset 47 | end 48 | return send_req(url) 49 | end 50 | 51 | function load_data(filename) 52 | local f = io.open(filename) 53 | if not f then 54 | return {} 55 | end 56 | local s = f:read('*all') 57 | f:close() 58 | local data = JSON.decode(s) 59 | return data 60 | end 61 | 62 | function save_data(filename, data) 63 | local s = JSON.encode(data) 64 | local f = io.open(filename, 'w') 65 | f:write(s) 66 | f:close() 67 | end 68 | 69 | function msg_valid(msg) 70 | local msg_time = os.time() - 60 71 | if msg.date < tonumber(msg_time) then 72 | print('\27[36m》》OLD MESSAGE《《\27[39m') 73 | return false 74 | end 75 | if is_silent_user(msg.from.id, msg.chat.id) then 76 | del_msg(msg.chat.id, tonumber(msg.message_id)) 77 | return false 78 | end 79 | if is_banned(msg.from.id, msg.chat.id) then 80 | del_msg(msg.chat.id, tonumber(msg.message_id)) 81 | kick_user(msg.from.id, msg.chat.id) 82 | return false 83 | end 84 | if is_gbanned(msg.from.id) then 85 | del_msg(msg.chat.id, tonumber(msg.message_id)) 86 | kick_user(msg.from.id, msg.chat.id) 87 | return false 88 | end 89 | return true 90 | end 91 | 92 | -- Returns a table with matches or nil 93 | function match_pattern(pattern, text, lower_case) 94 | if text then 95 | local matches = {} 96 | if lower_case then 97 | matches = { string.match(text:lower(), pattern) } 98 | else 99 | matches = { string.match(text, pattern) } 100 | end 101 | if next(matches) then 102 | return matches 103 | end 104 | end 105 | -- nil 106 | end 107 | plugins = {} 108 | function match_plugins(msg) 109 | for name, plugin in pairs(plugins) do 110 | match_plugin(plugin, name, msg) 111 | end 112 | end 113 | 114 | function match_plugin(plugin, plugin_name, msg) 115 | 116 | -- Go over patterns. If one matches it's enough. 117 | if plugin.pre_process then 118 | --If plugin is for privileged users only 119 | local result = plugin.pre_process(msg) 120 | if result then 121 | print("pre process: ", plugin_name) 122 | end 123 | end 124 | for k, pattern in pairs(plugin.patterns) do 125 | local matches = match_pattern(pattern, msg.text or msg.caption or msg.query) 126 | if matches then 127 | if not is_BDChannel_member(msg.from.id, msg.chat.id, msg.message_id) then 128 | local hash = "group_lang:"..msg.chat.id 129 | local lang = redis:get(hash) 130 | keyboard = {} 131 | keyboard.inline_keyboard = { 132 | { 133 | {text= 'Beyond Team Channel' ,url = 'Telegram.Me/BeyondTeam'} 134 | } 135 | } 136 | if lang then 137 | tkey = '_ابتدا در کانال تیم بیوند عضو شوید و دوباره تلاش کنید_' 138 | else 139 | tkey = '_First Join To_ *Beyond Team Channel* _And Try Again_' 140 | end 141 | send_key(msg.chat.id, tkey, keyboard, msg.message_id, "md") 142 | return 143 | end 144 | print("msg matches: ", pattern) 145 | -- Function exists 146 | if plugin.run then 147 | -- If plugin is for privileged users only 148 | local result = plugin.run(msg, matches) 149 | if result then 150 | send_msg(msg.chat.id, result, msg.message_id, "md") 151 | end 152 | end 153 | end 154 | end 155 | end 156 | local function handle_inline_keyboards_cb(msg) 157 | msg.text = '###cb:'..msg.data 158 | msg.cb = true 159 | if msg.new_chat_member then 160 | msg.service = true 161 | msg.text = '###new_chat_member' 162 | end 163 | if msg.message then 164 | msg.old_text = msg.message.text 165 | msg.old_date = msg.message.date 166 | msg.message_id = msg.message.message_id 167 | msg.chat = msg.message.chat 168 | msg.message_id = msg.message.message_id 169 | msg.chat = msg.message.chat 170 | else -- if keyboard send via 171 | msg.chat = {type = 'inline', id = msg.from.id, title = msg.from.first_name} 172 | msg.message_id = msg.inline_message_id 173 | end 174 | msg.cb_id = msg.id 175 | msg.date = os.time() 176 | msg.message = nil 177 | msg.target_id = msg.data:match('.*:(-?%d+)') 178 | return get_var(msg) 179 | end 180 | -- Save the content of _config to config.lua 181 | function save_config( ) 182 | serialize_to_file(_config, './data/config.lua') 183 | print ('saved config into ./data/config.lua') 184 | end 185 | 186 | -- Create a basic config.json file and saves it. 187 | function create_config( ) 188 | io.write('\n\27[1;33m>> Input your Telegram ID for set Sudo :\27[0;39;49m') 189 | local SUDO = tonumber(io.read()) 190 | if not tostring(SUDO):match('%d+') then 191 | SUDO = 157059515 192 | end 193 | -- A simple config with basic plugins and ourselves as privileged user 194 | config = { 195 | enabled_plugins = { 196 | "plugins", 197 | "msg_checks", 198 | "groupmanager", 199 | "tools", 200 | "banhammer", 201 | "fun" 202 | }, 203 | sudo_users = {157059515, SUDO},--Sudo users 204 | master_id = SUDO, 205 | admins = {}, 206 | disabled_channels = {}, 207 | moderation = {data = './data/moderation.json'}, 208 | info_text = [[*》Beyond Manager V2.0* 209 | `》An advanced administration bot based on` *BDMessenger* 210 | 211 | 》[Beyond Manager](https://github.com/BeyondTeam/BDManager) 212 | 213 | *》Admins :* 214 | *》Founder & Developer :* [SoLiD](Telegram.Me/SoLiD) 215 | _》Developer & Sponser :_ [MAKAN](Telegram.Me/MAKAN) 216 | _》Developer :_ [ToOfan](Telegram.Me/ToOfan) 217 | _》Developer :_ [TheNIS](Telegram.Me/bypa3r) 218 | 219 | *》Special thanks to :* 220 | `Beyond Team Members` 221 | 222 | *》Our channel :* 223 | 》[BeyondTeam](Telegram.Me/BeyondTeam) 224 | 225 | *》Our Site :* 226 | [Beyond Team Forum](beyond-dev.ir) 227 | ]], 228 | } 229 | serialize_to_file(config, './data/config.lua') 230 | print('saved config into ./data/config.lua') 231 | end 232 | 233 | -- Returns the config from config.lua file. 234 | -- If file doesn't exist, create it. 235 | function load_config( ) 236 | local f = io.open('./data/config.lua', "r") 237 | -- If config.lua doesn't exist 238 | if not f then 239 | print ("Created new config file: data/config.lua") 240 | create_config() 241 | else 242 | f:close() 243 | end 244 | local config = loadfile ("./data/config.lua")() 245 | for v,user in pairs(config.sudo_users) do 246 | print("Sudo user: " .. user) 247 | end 248 | return config 249 | end 250 | _config = load_config( ) 251 | 252 | -- Enable plugins in config.json 253 | function load_plugins() 254 | for k, v in pairs(_config.enabled_plugins) do 255 | print("Loading plugin", v) 256 | 257 | local ok, err = pcall(function() 258 | local t = loadfile("plugins/"..v..'.lua')() 259 | plugins[v] = t 260 | end) 261 | 262 | if not ok then 263 | print('\27[31mError loading plugin '..v..'\27[39m') 264 | print(tostring(io.popen("lua plugins/"..v..".lua"):read('*all'))) 265 | print('\27[31m'..err..'\27[39m') 266 | end 267 | 268 | end 269 | end 270 | 271 | bot_run() 272 | load_plugins() 273 | while startbot do 274 | local res = bot_updates(last_update+1) 275 | if res then 276 | for i,v in ipairs(res.result) do 277 | last_update = v.update_id 278 | if v.edited_message then 279 | get_var(v.edited_message) 280 | elseif v.message then 281 | if msg_valid(v.message) then 282 | get_var(v.message) 283 | end 284 | elseif v.inline_query then 285 | get_var_inline(v.inline_query) 286 | elseif v.callback_query then 287 | handle_inline_keyboards_cb(v.callback_query) 288 | end 289 | end 290 | else 291 | print("error while") 292 | end 293 | if last_cron < os.time() - 30 then 294 | for name, plugin in pairs(plugins) do 295 | if plugin.cron then 296 | local res, err = pcall( 297 | function() 298 | plugin.cron() 299 | end 300 | 301 | ) 302 | end 303 | if not res then print('error: '..err) end 304 | end 305 | last_cron = os.time() 306 | end 307 | end 308 | 309 | 310 | -------------------------------------------------------------------------------- /plugins/msg_checks.lua: -------------------------------------------------------------------------------- 1 | --Begin msg_checks.lua By @SoLiD 2 | local function pre_process(msg) 3 | if not msg.query then 4 | local status = getChatAdministrators(msg.to.id) 5 | local data = load_data(_config.moderation.data) 6 | local chat = msg.to.id 7 | local user = msg.from.id 8 | local is_channel = msg.to.type == "supergroup" 9 | local is_chat = msg.to.type == "group" 10 | local auto_leave = 'AutoLeaveBot' 11 | local TIME_CHECK = 2 12 | if data[tostring(chat)] then 13 | if data[tostring(chat)]['settings']['time_check'] then 14 | TIME_CHECK = tonumber(data[tostring(chat)]['settings']['time_check']) 15 | end 16 | end 17 | if is_channel or is_chat then 18 | if msg.text then 19 | if msg.text:match("(.*)") then 20 | if not data[tostring(msg.to.id)] and not redis:get(auto_leave) and not is_admin(msg) then 21 | send_msg(msg.to.id, "_This Is Not One Of My_ *Groups*", nil, "md") 22 | leave_group(chat) 23 | end 24 | end 25 | end 26 | if data[tostring(chat)] and data[tostring(chat)]['mutes'] then 27 | mutes = data[tostring(chat)]['mutes'] 28 | else 29 | return 30 | end 31 | if mutes.mute_all then 32 | mute_all = mutes.mute_all 33 | else 34 | mute_all = 'no' 35 | end 36 | if mutes.mute_gif then 37 | mute_gif = mutes.mute_gif 38 | else 39 | mute_gif = 'no' 40 | end 41 | if mutes.mute_photo then 42 | mute_photos = mutes.mute_photo 43 | else 44 | mute_photos = 'no' 45 | end 46 | if mutes.mute_sticker then 47 | mute_sticker = mutes.mute_sticker 48 | else 49 | mute_sticker = 'no' 50 | end 51 | if mutes.mute_contact then 52 | mute_contact = mutes.mute_contact 53 | else 54 | mute_contact = 'no' 55 | end 56 | if mutes.mute_text then 57 | mute_text = mutes.mute_text 58 | else 59 | mute_text = 'no' 60 | end 61 | if mutes.mute_forward then 62 | mute_forward = mutes.mute_forward 63 | else 64 | mute_forward = 'no' 65 | end 66 | if mutes.mute_location then 67 | mute_location = mutes.mute_location 68 | else 69 | mute_location = 'no' 70 | end 71 | if mutes.mute_document then 72 | mute_document = mutes.mute_document 73 | else 74 | mute_document = 'no' 75 | end 76 | if mutes.mute_voice then 77 | mute_voice = mutes.mute_voice 78 | else 79 | mute_voice = 'no' 80 | end 81 | if mutes.mute_audio then 82 | mute_audio = mutes.mute_audio 83 | else 84 | mute_audio = 'no' 85 | end 86 | if mutes.mute_video then 87 | mute_video = mutes.mute_video 88 | else 89 | mute_video = 'no' 90 | end 91 | if mutes.mute_tgservice then 92 | mute_tgservice = mutes.mute_tgservice 93 | else 94 | mute_tgservice = 'no' 95 | end 96 | if data[tostring(chat)] and data[tostring(chat)]['settings'] then 97 | settings = data[tostring(chat)]['settings'] 98 | else 99 | return 100 | end 101 | if settings.lock_link then 102 | lock_link = settings.lock_link 103 | else 104 | lock_link = 'no' 105 | end 106 | if settings.lock_bots then 107 | lock_bot = settings.lock_bots 108 | else 109 | lock_bot = 'no' 110 | end 111 | if settings.lock_tag then 112 | lock_tag = settings.lock_tag 113 | else 114 | lock_tag = 'no' 115 | end 116 | if settings.lock_pin then 117 | lock_pin = settings.lock_pin 118 | else 119 | lock_pin = 'no' 120 | end 121 | if settings.lock_mention then 122 | lock_mention = settings.lock_mention 123 | else 124 | lock_mention = 'no' 125 | end 126 | if settings.lock_edit then 127 | lock_edit = settings.lock_edit 128 | else 129 | lock_edit = 'no' 130 | end 131 | if settings.lock_spam then 132 | lock_spam = settings.lock_spam 133 | else 134 | lock_spam = 'no' 135 | end 136 | if settings.flood then 137 | lock_flood = settings.flood 138 | else 139 | lock_flood = 'no' 140 | end 141 | if settings.lock_markdown then 142 | lock_markdown = settings.lock_markdown 143 | else 144 | lock_markdown = 'no' 145 | end 146 | if settings.lock_webpage then 147 | lock_webpage = settings.lock_webpage 148 | else 149 | lock_webpage = 'no' 150 | end 151 | 152 | if msg.newuser then 153 | if msg.newuser.username ~= nil then 154 | if string.sub(msg.newuser.username:lower(), -3) == 'bot' and not is_owner(msg) and lock_bot == "yes" then 155 | kick_user(msg.newuser.id, chat) 156 | end 157 | end 158 | end 159 | if msg.service and mute_tgservice == "yes" then 160 | del_msg(chat, tonumber(msg.id)) 161 | end 162 | if not msg.cb and not is_mod(msg) and not is_whitelist(msg.from.id, msg.to.id) then 163 | if msg.pinned_message and is_channel then 164 | if lock_pin == "yes" and not is_owner(msg) then 165 | local pin_msg = data[tostring(msg.to.id)]['pin'] 166 | if pin_msg then 167 | pinChatMessage(msg.to.id, pin_msg) 168 | elseif not pin_msg then 169 | unpinChatMessage(msg.to.id) 170 | end 171 | send_msg(msg.to.id, 'User ID : '..msg.from.id..'\nUsername : '..('@'..msg.from.username or 'No Username')..'\nYou Have Not Permission To Pin Message, Last Message Has Been Pinned Again', msg.id, "html") 172 | end 173 | end 174 | if msg.message_edited and lock_edit == "yes" then 175 | if is_channel then 176 | del_msg(chat, tonumber(msg.id)) 177 | elseif is_chat then 178 | kick_user(user, chat) 179 | end 180 | end 181 | if msg.fwd_from and mute_forward == "yes" then 182 | if is_channel then 183 | del_msg(chat, tonumber(msg.id)) 184 | elseif is_chat then 185 | kick_user(user, chat) 186 | end 187 | end 188 | if msg.photo and mute_photos == "yes" then 189 | if is_channel then 190 | del_msg(chat, tonumber(msg.id)) 191 | elseif is_chat then 192 | kick_user(user, chat) 193 | end 194 | end 195 | if msg.video and mute_video == "yes" then 196 | if is_channel then 197 | del_msg(chat, tonumber(msg.id)) 198 | elseif is_chat then 199 | kick_user(user, chat) 200 | end 201 | end 202 | if msg.document and mute_document == "yes" and msg.document.mime_type ~= "audio/mpeg" and msg.document.mime_type ~= "video/mp4" then 203 | if is_channel then 204 | del_msg(chat, tonumber(msg.id)) 205 | elseif is_chat then 206 | kick_user(user, chat) 207 | end 208 | end 209 | if msg.sticker and mute_sticker == "yes" then 210 | if is_channel then 211 | del_msg(chat, tonumber(msg.id)) 212 | elseif is_chat then 213 | kick_user(user, chat) 214 | end 215 | end 216 | if msg.document and msg.document.mime_type == "video/mp4" and mute_gif == "yes" then 217 | if is_channel then 218 | del_msg(chat, tonumber(msg.id)) 219 | elseif is_chat then 220 | kick_user(user, chat) 221 | end 222 | end 223 | if msg.contact and mute_contact == "yes" then 224 | if is_channel then 225 | del_msg(chat, tonumber(msg.id)) 226 | elseif is_chat then 227 | kick_user(user, chat) 228 | end 229 | end 230 | if msg.location and mute_location == "yes" then 231 | if is_channel then 232 | del_msg(chat, tonumber(msg.id)) 233 | elseif is_chat then 234 | kick_user(user, chat) 235 | end 236 | end 237 | if msg.voice and mute_voice == "yes" then 238 | if is_channel then 239 | del_msg(chat, tonumber(msg.id)) 240 | elseif is_chat then 241 | kick_user(user, chat) 242 | end 243 | end 244 | if msg.document and msg.document.mime_type == "audio/mpeg" and mute_audio == "yes" then 245 | if is_channel then 246 | del_msg(chat, tonumber(msg.id)) 247 | elseif is_chat then 248 | kick_user(user, chat) 249 | end 250 | end 251 | if msg.caption then 252 | local link_caption = msg.caption:match("[Tt][Ee][Ll][Ee][Gg][Rr][Aa][Mm].[Mm][Ee]/") or msg.caption:match("[Tt][Ee][Ll][Ee][Gg][Rr][Aa][Mm].[Dd][Oo][Gg]/") or msg.caption:match("[Tt].[Mm][Ee]/") or msg.caption:match("[Tt][Ll][Gg][Rr][Mm].[Mm][Ee]/") 253 | if link_caption 254 | and lock_link == "yes" then 255 | if is_channel then 256 | del_msg(chat, tonumber(msg.id)) 257 | elseif is_chat then 258 | kick_user(user, chat) 259 | end 260 | end 261 | local tag_caption = msg.caption:match("@") or msg.caption:match("#") 262 | if tag_caption and lock_tag == "yes" then 263 | if is_channel then 264 | del_msg(chat, tonumber(msg.id)) 265 | elseif is_chat then 266 | kick_user(user, chat) 267 | end 268 | end 269 | if is_filter(msg, msg.caption) then 270 | if is_channel then 271 | del_msg(chat, tonumber(msg.id)) 272 | elseif is_chat then 273 | kick_user(user, chat) 274 | end 275 | end 276 | end 277 | if msg.text then 278 | local _nl, ctrl_chars = string.gsub(msg.text, '%c', '') 279 | local max_chars = 40 280 | if data[tostring(msg.to.id)] then 281 | if data[tostring(msg.to.id)]['settings']['set_char'] then 282 | max_chars = tonumber(data[tostring(msg.to.id)]['settings']['set_char']) 283 | end 284 | end 285 | local _nl, real_digits = string.gsub(msg.text, '%d', '') 286 | local max_real_digits = tonumber(max_chars) * 50 287 | local max_len = tonumber(max_chars) * 51 288 | if lock_spam == "yes" then 289 | if string.len(msg.text) > max_len or ctrl_chars > max_chars or real_digits > max_real_digits then 290 | if is_channel then 291 | del_msg(chat, tonumber(msg.id)) 292 | elseif is_chat then 293 | kick_user(user, chat) 294 | end 295 | end 296 | end 297 | local link_msg = msg.text:match("[Tt][Ee][Ll][Ee][Gg][Rr][Aa][Mm].[Mm][Ee]/") or msg.text:match("[Tt][Ee][Ll][Ee][Gg][Rr][Aa][Mm].[Dd][Oo][Gg]/") or msg.text:match("[Tt].[Mm][Ee]/") or msg.text:match("[Tt][Ll][Gg][Rr][Mm].[Mm][Ee]/") 298 | if link_msg 299 | and lock_link == "yes" then 300 | if is_channel then 301 | del_msg(chat, tonumber(msg.id)) 302 | elseif is_chat then 303 | kick_user(user, chat) 304 | end 305 | end 306 | local tag_msg = msg.text:match("@") or msg.text:match("#") 307 | if tag_msg and lock_tag == "yes" then 308 | if is_channel then 309 | del_msg(chat, tonumber(msg.id)) 310 | elseif is_chat then 311 | kick_user(user, chat) 312 | end 313 | end 314 | if is_filter(msg, msg.text) then 315 | if is_channel then 316 | del_msg(chat, tonumber(msg.id)) 317 | elseif is_chat then 318 | kick_user(user, chat) 319 | end 320 | end 321 | 322 | if msg.text:match("(.*)") 323 | and mute_text == "yes" then 324 | if is_channel then 325 | del_msg(chat, tonumber(msg.id)) 326 | elseif is_chat then 327 | kick_user(user, chat) 328 | end 329 | end 330 | end 331 | if mute_all == "yes" then 332 | if is_channel then 333 | del_msg(chat, tonumber(msg.id)) 334 | elseif is_chat then 335 | kick_user(user, chat) 336 | end 337 | end 338 | if msg.entities then 339 | for i,entity in pairs(msg.entities) do 340 | if entity.type == "text_mention" then 341 | if lock_mention == "yes" then 342 | if is_channel then 343 | del_msg(chat, tonumber(msg.id)) 344 | elseif is_chat then 345 | kick_user(user, chat) 346 | end 347 | end 348 | end 349 | if entity.type == "url" or entity.type == "text_link" then 350 | if lock_webpage == "yes" then 351 | if is_channel then 352 | del_msg(chat, tonumber(msg.id)) 353 | elseif is_chat then 354 | kick_user(user, chat) 355 | end 356 | end 357 | end 358 | if entity.type == "bold" or entity.type == "code" or entity.type == "italic" then 359 | if lock_markdown == "yes" then 360 | if is_channel then 361 | del_msg(chat, tonumber(msg.id)) 362 | elseif is_chat then 363 | kick_user(user, chat) 364 | end 365 | end 366 | end 367 | end 368 | end 369 | if msg.to.type ~= 'private' and not is_mod(msg) and not is_whitelist(msg.from.id, msg.to.id) then 370 | if lock_flood == "yes" then 371 | local hash = 'user:'..user..':msgs' 372 | local msgs = tonumber(redis:get(hash) or 0) 373 | local NUM_MSG_MAX = 5 374 | if data[tostring(chat)] then 375 | if data[tostring(chat)]['settings']['num_msg_max'] then 376 | NUM_MSG_MAX = tonumber(data[tostring(chat)]['settings']['num_msg_max']) 377 | end 378 | end 379 | if msgs > NUM_MSG_MAX then 380 | if msg.from.username then 381 | user_name = "@"..check_markdown(msg.from.username) 382 | else 383 | user_name = escape_markdown(msg.from.first_name) 384 | end 385 | if redis:get('sender:'..user..':flood') then 386 | return 387 | else 388 | del_msg(chat, msg.id) 389 | kick_user(user, chat) 390 | del_msg(chat, msg.id) 391 | send_msg(chat, "_User_ "..user_name.." `[ "..user.." ]` _has been_ *kicked* _because of_ *flooding*", nil, "md") 392 | redis:setex('sender:'..user..':flood', 30, true) 393 | end 394 | end 395 | redis:setex(hash, TIME_CHECK, msgs+1) 396 | end 397 | end 398 | end 399 | end 400 | end 401 | end 402 | return { 403 | patterns = {}, 404 | pre_process = pre_process 405 | } 406 | --End msg_checks.lua @BeyondTeam 407 | -------------------------------------------------------------------------------- /plugins/fun.lua: -------------------------------------------------------------------------------- 1 | 2 | --Begin Fun.lua By @BeyondTeam 3 | --Special Thx To @ToOfan 4 | -------------------------------- 5 | 6 | local function run_bash(str) 7 | local cmd = io.popen(str) 8 | local result = cmd:read('*all') 9 | return result 10 | end 11 | -------------------------------- 12 | local api_key = nil 13 | local base_api = "https://maps.googleapis.com/maps/api" 14 | -------------------------------- 15 | local function get_latlong(area) 16 | local api = base_api .. "/geocode/json?" 17 | local parameters = "address=".. (URL.escape(area) or "") 18 | if api_key ~= nil then 19 | parameters = parameters .. "&key="..api_key 20 | end 21 | local res, code = https.request(api..parameters) 22 | if code ~=200 then return nil end 23 | local data = json:decode(res) 24 | if (data.status == "ZERO_RESULTS") then 25 | return nil 26 | end 27 | if (data.status == "OK") then 28 | lat = data.results[1].geometry.location.lat 29 | lng = data.results[1].geometry.location.lng 30 | acc = data.results[1].geometry.location_type 31 | types= data.results[1].types 32 | return lat,lng,acc,types 33 | end 34 | end 35 | -------------------------------- 36 | local function get_staticmap(area) 37 | local api = base_api .. "/staticmap?" 38 | local lat,lng,acc,types = get_latlong(area) 39 | local scale = types[1] 40 | if scale == "locality" then 41 | zoom=8 42 | elseif scale == "country" then 43 | zoom=4 44 | else 45 | zoom = 13 46 | end 47 | local parameters = 48 | "size=600x300" .. 49 | "&zoom=" .. zoom .. 50 | "¢er=" .. URL.escape(area) .. 51 | "&markers=color:red"..URL.escape("|"..area) 52 | if api_key ~= nil and api_key ~= "" then 53 | parameters = parameters .. "&key="..api_key 54 | end 55 | return lat, lng, api..parameters 56 | end 57 | -------------------------------- 58 | local function get_weather(msg, location) 59 | local hash = "group_lang:"..msg.to.id 60 | local lang = redis:get(hash) 61 | print("Finding weather in ", location) 62 | local BASE_URL = "http://api.openweathermap.org/data/2.5/weather" 63 | local url = BASE_URL 64 | url = url..'?q='..location..'&APPID=eedbc05ba060c787ab0614cad1f2e12b' 65 | url = url..'&units=metric' 66 | local b, c, h = http.request(url) 67 | if c ~= 200 then return nil end 68 | local weather = json:decode(b) 69 | local city = weather.name 70 | local country = weather.sys.country 71 | if not lang then 72 | temp = 'The temperature of '..city..' is now '..weather.main.temp..' ° C\n____________________' 73 | else 74 | temp = 'دمای شهر '..city..' هم اکنون '..weather.main.temp..' درجه سانتی گراد می باشد\n____________________' 75 | end 76 | if not lang then 77 | conditions = 'Current weather conditions : ' 78 | else 79 | conditions = 'شرایط فعلی آب و هوا : ' 80 | end 81 | if weather.weather[1].main == 'Clear' then 82 | if not lang then 83 | conditions = conditions .. 'Sunny☀' 84 | else 85 | conditions = conditions .. 'آفتابی☀' 86 | end 87 | elseif weather.weather[1].main == 'Clouds' then 88 | if not lang then 89 | conditions = conditions .. 'Cloudy ☁☁' 90 | else 91 | conditions = conditions .. 'ابری ☁☁' 92 | end 93 | elseif weather.weather[1].main == 'Rain' then 94 | if not lang then 95 | conditions = conditions .. 'Rainy ☔' 96 | else 97 | conditions = conditions .. 'بارانی ☔' 98 | end 99 | elseif weather.weather[1].main == 'Thunderstorm' then 100 | if not lang then 101 | conditions = conditions .. 'Stormy ☔☔☔☔' 102 | else 103 | conditions = conditions .. 'طوفانی ☔☔☔☔' 104 | end 105 | elseif weather.weather[1].main == 'Mist' then 106 | if not lang then 107 | conditions = conditions .. 'Mist 💨' 108 | else 109 | conditions = conditions .. 'مه 💨' 110 | end 111 | end 112 | return temp .. '\n' .. conditions..'\n@BeyondTeam' 113 | end 114 | -------------------------------- 115 | local function calc(msg, exp) 116 | local hash = "group_lang:"..msg.to.id 117 | local lang = redis:get(hash) 118 | url = 'http://api.mathjs.org/v1/' 119 | url = url..'?expr='..URL.escape(exp) 120 | b,c = http.request(url) 121 | text = nil 122 | if c == 200 then 123 | if not lang then 124 | text = 'Result = '..b..'\n____________________\n@BeyondTeam' 125 | else 126 | text = 'نتیجه = '..b..'\n____________________\n@BeyondTeam' 127 | end 128 | elseif c == 400 then 129 | text = b 130 | else 131 | text = 'Unexpected error\n' 132 | ..'Is api.mathjs.org up?' 133 | end 134 | return text 135 | end 136 | -------------------------------- 137 | function run(msg, matches) 138 | local hash = "group_lang:"..msg.to.id 139 | local lang = redis:get(hash) 140 | if matches[1]:lower() == 'calc' and matches[2] then 141 | if msg.to.type == "private" then 142 | return 143 | end 144 | return calc(msg, matches[2]) 145 | end 146 | -------------------------------- 147 | if matches[1]:lower() == 'praytime' then 148 | if matches[2] then 149 | city = matches[2] 150 | elseif not matches[2] then 151 | city = 'Tehran' 152 | end 153 | local lat,lng,url = get_staticmap(city) 154 | local dumptime = run_bash('date +%s') 155 | local code = http.request('http://api.aladhan.com/timings/'..dumptime..'?latitude='..lat..'&longitude='..lng..'&timezonestring=Asia/Tehran&method=7') 156 | local jdat = json:decode(code) 157 | local data = jdat.data.timings 158 | if not lang then 159 | text = 'City: '..city 160 | text = text..'\nMorning Azan: '..data.Fajr 161 | text = text..'\nSunrise: '..data.Sunrise 162 | text = text..'\nNoon Azan: '..data.Dhuhr 163 | text = text..'\nSunset: '..data.Sunset 164 | text = text..'\nMaghrib Azan: '..data.Maghrib 165 | text = text..'\nIsha: '..data.Isha 166 | text = text.."\n@BeyondTeam" 167 | else 168 | text = 'شهر: '..city 169 | text = text..'\nاذان صبح: '..data.Fajr 170 | text = text..'\nطلوع آفتاب: '..data.Sunrise 171 | text = text..'\nاذان ظهر: '..data.Dhuhr 172 | text = text..'\nغروب آفتاب: '..data.Sunset 173 | text = text..'\nاذان مغرب: '..data.Maghrib 174 | text = text..'\nعشاء : '..data.Isha 175 | text = text.."\n@BeyondTeam" 176 | end 177 | return text 178 | end 179 | -------------------------------- 180 | if matches[1] == 'tosticker' then 181 | if msg.reply_to_message then 182 | if msg.reply_to_message.photo then 183 | if msg.reply_to_message.photo[3] then 184 | fileid = msg.reply_to_message.photo[3].file_id 185 | elseif msg.reply_to_message.photo[2] then 186 | fileid = msg.reply_to_message.photo[2].file_id 187 | else 188 | fileid = msg.reply_to_message.photo[1].file_id 189 | end 190 | downloadFile(fileid, "./download_path/"..msg.to.id..".webp") 191 | sleep(1) 192 | sendDocument(msg.to.id, "./download_path/"..msg.to.id..".webp", msg.id, "@BeyondTeam") 193 | end 194 | end 195 | end 196 | -------------------------------- 197 | if matches[1] == 'tophoto' then 198 | if msg.reply_to_message then 199 | if msg.reply_to_message.sticker then 200 | downloadFile(msg.reply_to_message.sticker.file_id, "./download_path/"..msg.to.id..".jpg") 201 | sleep(1) 202 | sendPhoto(msg.to.id, "./download_path/"..msg.to.id..".jpg", "@BeyondTeam", msg.id) 203 | end 204 | end 205 | end 206 | -------------------------------- 207 | if matches[1]:lower() == 'weather' then 208 | city = matches[2]:lower() 209 | local wtext = get_weather(msg, city) 210 | if not wtext then 211 | if not lang then 212 | wtext = 'Imported location is not correct' 213 | else 214 | wtext = 'مکان وارد شده صحیح نیست' 215 | end 216 | end 217 | return wtext 218 | end 219 | -------------------------------- 220 | if matches[1]:lower() == 'time' then 221 | local url , res = http.request('http://api.beyond-dev.ir/time/') 222 | if res ~= 200 then 223 | return "No connection" 224 | end 225 | local colors = {'blue','green','yellow','magenta','Orange','DarkOrange','red'} 226 | local fonts = {'mathbf','mathit','mathfrak','mathrm'} 227 | local jdat = json:decode(url) 228 | local url = 'http://latex.codecogs.com/png.download?'..'\\dpi{600}%20\\huge%20\\'..fonts[math.random(#fonts)]..'{{\\color{'..colors[math.random(#colors)]..'}'..jdat.ENtime..'}}' 229 | local file = download_to_file(url,'time.webp') 230 | sendDocument(msg.to.id, file, msg.id, "@BeyondTeam") 231 | 232 | end 233 | -------------------------------- 234 | if matches[1]:lower() == 'voice' then 235 | local text = matches[2] 236 | textc = text:gsub(' ','.') 237 | 238 | if msg.to.type == 'private' then 239 | return nil 240 | else 241 | local url = "http://tts.baidu.com/text2audio?lan=en&ie=UTF-8&text="..textc 242 | local file = download_to_file(url,'BD-Manager.mp3') 243 | sendDocument(msg.to.id, file, msg.id, "@BeyondTeam") 244 | end 245 | end 246 | 247 | -------------------------------- 248 | if matches[1]:lower() == 'tr' then 249 | local to = matches[2] 250 | local res, url = http.request('http://api.beyond-dev.ir/translate?from=.&to='..to..'&text='..URL.escape(matches[3])..'&simple=json') 251 | data = json:decode(res) 252 | if not lang then 253 | return 'Language : '..data.to..'\nTranslation : '..data.translate..'\n____________________\n@BeyondTeam' 254 | else 255 | return 'زبان : '..data.to..'\nترجمه : '..data.translate..'\n____________________\n@BeyondTeam' 256 | end 257 | end 258 | -------------------------------- 259 | if matches[1]:lower() == 'short' then 260 | local longlink = http.request('http://api.beyond-dev.ir/shortLink?url='..matches[2]) 261 | local shlink = json:decode(longlink) 262 | if shlink.status then 263 | if not lang then 264 | return 'Short Links:\nGoogle: '..(shlink.results.google or '')..'\nOpizo: '..(shlink.results.opizo or '')..'\nBitly: '..(shlink.results.bitly or '')..'\nYon: '..(shlink.results.yon or '')..'\nLlink: '..(shlink.results.llink or '')..'\nU2S: '..(shlink.results.u2s or '')..'\nShorte: '..(shlink.results.shorte or '') 265 | else 266 | return 'لینک های کوتاه شده :\nGoogle: '..(shlink.results.google or '')..'\nOpizo: '..(shlink.results.opizo or '')..'\nBitly: '..(shlink.results.bitly or '')..'\nYon: '..(shlink.results.yon or '')..'\nLlink: '..(shlink.results.llink or '')..'\nU2S: '..(shlink.results.u2s or '')..'\nShorte: '..(shlink.results.shorte or '') 267 | end 268 | else 269 | if not lang then 270 | return '_Input Correct Link!_' 271 | else 272 | return '_لینک صحیح را وارد کنید!_' 273 | end 274 | end 275 | end 276 | -------------------------------- 277 | if matches[1]:lower() == 'sticker' then 278 | local eq = URL.escape(matches[2]) 279 | local w = "500" 280 | local h = "500" 281 | local txtsize = "100" 282 | local txtclr = "ff2e4357" 283 | if matches[3] then 284 | txtclr = matches[3] 285 | end 286 | if matches[4] then 287 | txtsize = matches[4] 288 | end 289 | if matches[5] and matches[6] then 290 | w = matches[5] 291 | h = matches[6] 292 | end 293 | local url = "https://assets.imgix.net/examples/clouds.jpg?blur=150&w="..w.."&h="..h.."&fit=crop&txt="..eq.."&txtsize="..txtsize.."&txtclr="..txtclr.."&txtalign=middle,center&txtfont=Futura%20Condensed%20Medium&mono=ff6598cc" 294 | local receiver = msg.to.id 295 | local file = download_to_file(url,'text.webp') 296 | sendDocument(msg.to.id, file, msg.id, "@BeyondTeam") 297 | end 298 | -------------------------------- 299 | if matches[1]:lower() == 'photo' then 300 | local eq = URL.escape(matches[2]) 301 | local w = "500" 302 | local h = "500" 303 | local txtsize = "100" 304 | local txtclr = "ff2e4357" 305 | if matches[3] then 306 | txtclr = matches[3] 307 | end 308 | if matches[4] then 309 | txtsize = matches[4] 310 | end 311 | if matches[5] and matches[6] then 312 | w = matches[5] 313 | h = matches[6] 314 | end 315 | local url = "https://assets.imgix.net/examples/clouds.jpg?blur=150&w="..w.."&h="..h.."&fit=crop&txt="..eq.."&txtsize="..txtsize.."&txtclr="..txtclr.."&txtalign=middle,center&txtfont=Futura%20Condensed%20Medium&mono=ff6598cc" 316 | local receiver = msg.to.id 317 | local file = download_to_file(url,'text.jpg') 318 | sendPhoto(msg.to.id, file, "@BeyondTeam", msg.id) 319 | end 320 | if matches[1] == 'fal' then 321 | local url , res = http.request('http://api.beyond-dev.ir/fal') 322 | if res ~= 200 then return "No connection" end 323 | local jdat = json:decode(url) 324 | if not lang then 325 | fal = "*Poems*:\n"..jdat.poem.."\n\n*Interpretation:*\n"..jdat.mean..'\n\n@BeyondTeam' 326 | else 327 | fal = "_غزل:_\n"..jdat.poem.."\n\n_تعبیر فال:_\n"..jdat.mean..'\n\n@BeyondTeam' 328 | end 329 | return fal 330 | end 331 | if matches[1] == 'bazaar' or matches[1] == 'fibazaar' then 332 | if matches[1] == 'bazaar' then 333 | xurl = 'http://api.beyond-dev.ir/bazaar/?q='..URL.escape(matches[2]) 334 | elseif matches[1] == 'fibazaar' then 335 | xurl = 'http://api.beyond-dev.ir/bazaar/?price=yes&q='..URL.escape(matches[2]) 336 | end 337 | local url , res = http.request(xurl) 338 | if res ~= 200 then return "No connection" end 339 | local jdat = json:decode(url) 340 | if not lang then 341 | bazaar = "Results:\n"..jdat.information 342 | else 343 | bazaar = "نتایج:\n"..jdat.information 344 | end 345 | return check_markdown(bazaar) 346 | end 347 | -------------------------------- 348 | if matches[1] == "helpfun" then 349 | if not lang then 350 | helpfun = [[ 351 | _Beyond Manager Fun Help Commands:_ 352 | 353 | *!time* 354 | _Get time in a sticker_ 355 | 356 | *!short* `[link]` 357 | _Make short url_ 358 | 359 | *!voice* `[text]` 360 | _Convert text to voice_ 361 | 362 | *!tr* `[lang] [word]` 363 | _Translates multy languages_ 364 | _Example:_ 365 | *!tr fa hi* 366 | 367 | *!sticker* `[word]` 368 | _Convert text to sticker_ 369 | 370 | *!photo* `[word]` 371 | _Convert text to photo_ 372 | 373 | *!calc* `[number]` 374 | Calculator 375 | 376 | *!praytime* `[city]` 377 | _Get Patent (Pray Time)_ 378 | 379 | *!tosticker* `[reply]` 380 | _Convert photo to sticker_ 381 | 382 | *!tophoto* `[reply]` 383 | _Convert text to photo_ 384 | 385 | *!weather* `[city]` 386 | _Get weather_ 387 | 388 | *!bazaar* `[App Name]` 389 | _Search in CafeBazaar market_ 390 | 391 | *!fibazaar* `[App Name]` 392 | _Search in CafeBazaar market with app price_ 393 | 394 | *!fal* 395 | _Fale Hafez_ 396 | 397 | _You can use_ *[!/#]* _at the beginning of commands._ 398 | 399 | *Good luck ;)*]] 400 | else 401 | 402 | helpfun = [[ 403 | _راهنمای فان ربات بیوند منیجر:_ 404 | 405 | *!time* 406 | _دریافت ساعت به صورت استیکر_ 407 | 408 | *!short* `[link]` 409 | _کوتاه کننده لینک_ 410 | 411 | *!voice* `[text]` 412 | _تبدیل متن به صدا_ 413 | 414 | *!tr* `[lang]` `[word]` 415 | _ترجمه به زبانهای مختلف_ 416 | _مثال:_ 417 | _!tr en سلام_ 418 | 419 | *!sticker* `[word]` 420 | _تبدیل متن به استیکر_ 421 | 422 | *!photo* `[word]` 423 | _تبدیل متن به عکس_ 424 | 425 | *!calc* `[number]` 426 | _ماشین حساب_ 427 | 428 | *!praytime* `[city]` 429 | _اعلام ساعات شرعی_ 430 | 431 | *!tosticker* `[reply]` 432 | _تبدیل عکس به استیکر_ 433 | 434 | *!tophoto* `[reply]` 435 | _تبدیل استیکر‌به عکس_ 436 | 437 | *!weather* `[city]` 438 | _دریافت اب وهوا_ 439 | 440 | *!bazaar* `[App Name]` 441 | _جستجو در برنامه های بازار_ 442 | 443 | *!fibazaar* `[App Name]` 444 | _جستجو در برنامه های بازار با درج قیمت برنامه_ 445 | 446 | *!fal* 447 | _فال حافظ_ 448 | 449 | *شما میتوانید از [!/#] در اول دستورات برای اجرای آنها بهره بگیرید* 450 | 451 | موفق باشید ;)]] 452 | end 453 | return helpfun.."\n@BeyondTeam" 454 | end 455 | 456 | end 457 | -------------------------------- 458 | return { 459 | patterns = { 460 | "^[!/#](helpfun)$", 461 | "^[!/#](weather) (.*)$", 462 | "^[!/#](calc) (.*)$", 463 | "^[#!/](time)$", 464 | "^[#!/](tophoto)$", 465 | "^[#!/](tosticker)$", 466 | "^[!/#](voice) +(.*)$", 467 | "^[/!#]([Pp]raytime) (.*)$", 468 | "^[/!#](praytime)$", 469 | "^[!/#]([Tt]r) ([^%s]+) (.*)$", 470 | "^[!/#]([Ss]hort) (.*)$", 471 | "^[!/#](photo) (.+)$", 472 | "^[!/#](sticker) (.+)$", 473 | '^[/!](fal)$', 474 | '^[/!](bazaar) (.*)$', 475 | '^[/!](fibazaar) (.*)$', 476 | }, 477 | run = run, 478 | } 479 | 480 | --#by @BeyondTeam :) 481 | -------------------------------------------------------------------------------- /bot/methods.lua: -------------------------------------------------------------------------------- 1 | -- @BeyondTeam 2 | function getUser(user_id) 3 | local url = http.request('https://www.api.beyond-dev.ir/getUser?token='..bot_token..'&user_id='..user_id) 4 | local user = JSON.decode(url) 5 | return user 6 | end 7 | 8 | function resolve_username(username) 9 | local url = http.request('https://www.api.beyond-dev.ir/getUser?token='..bot_token..'&username='..username) 10 | local user = JSON.decode(url) 11 | return user 12 | end 13 | 14 | function send_msg(chat_id, text, reply_to_message_id, markdown) 15 | 16 | local url = send_api .. '/sendMessage?chat_id=' .. chat_id .. '&text=' .. URL.escape(text) 17 | 18 | if reply_to_message_id then 19 | url = url .. '&reply_to_message_id=' .. reply_to_message_id 20 | end 21 | 22 | if markdown == 'md' or markdown == 'markdown' then 23 | url = url..'&parse_mode=Markdown' 24 | elseif markdown == 'html' then 25 | url = url..'&parse_mode=HTML' 26 | end 27 | 28 | return send_req(url) 29 | 30 | end 31 | 32 | function edit(chat_id, message_id, text, keyboard, markdown) 33 | 34 | local url = send_api .. '/editMessageText?chat_id=' .. chat_id .. '&message_id='..message_id..'&text=' .. URL.escape(text) 35 | 36 | if markdown then 37 | url = url .. '&parse_mode=Markdown' 38 | end 39 | 40 | url = url .. '&disable_web_page_preview=true' 41 | 42 | if keyboard then 43 | url = url..'&reply_markup='..JSON.encode(keyboard) 44 | end 45 | 46 | return send_req(url) 47 | 48 | end 49 | function send(chat_id, text, keyboard, markdown) 50 | 51 | local url = send_api.. '/sendMessage?chat_id=' .. chat_id 52 | 53 | if markdown then 54 | url = url .. '&parse_mode=Markdown' 55 | end 56 | 57 | url = url..'&text='..URL.escape(text) 58 | 59 | url = url..'&reply_markup='..JSON.encode(keyboard) 60 | 61 | return send_req(url) 62 | 63 | end 64 | function send_document(chat_id, name) 65 | local send = send_api.."/sendDocument" 66 | local curl_command = 'curl -s "'..send..'" -F "chat_id='..chat_id..'" -F "document=@'..name..'"' 67 | return io.popen(curl_command):read("*all") 68 | end 69 | 70 | function fwd_msg(chat_id, from_chat_id, message_id) 71 | 72 | local url = send_api .. '/forwardMessage?chat_id=' .. chat_id .. '&from_chat_id=' .. from_chat_id .. '&message_id=' .. message_id 73 | 74 | return send_req(url) 75 | 76 | end 77 | function edit_key(chat_id, message_id, reply_markup) 78 | 79 | local url = send_api .. '/editMessageReplyMarkup?chat_id=' .. chat_id .. 80 | '&message_id='..message_id.. 81 | '&reply_markup='..URL.escape(JSON.encode(reply_markup)) 82 | 83 | return send_req(url) 84 | 85 | end 86 | function send_key(chat_id, text, keyboard, reply_to_message_id, markdown) 87 | 88 | local url = send_api .. '/sendMessage?chat_id=' .. chat_id 89 | 90 | if reply_to_message_id then 91 | url = url .. '&reply_to_message_id=' .. reply_to_message_id 92 | end 93 | 94 | if markdown == 'md' or markdown == 'markdown' then 95 | url = url..'&parse_mode=Markdown' 96 | elseif markdown == 'html' then 97 | url = url..'&parse_mode=HTML' 98 | end 99 | 100 | url = url..'&text='..URL.escape(text) 101 | 102 | url = url..'&disable_web_page_preview=true' 103 | 104 | url = url..'&reply_markup='..URL.escape(JSON.encode(keyboard)) 105 | return send_req(url) 106 | end 107 | 108 | function edit_msg(chat_id, message_id, text, keyboard, markdown) 109 | 110 | local url = send_api .. '/editMessageText?chat_id=' .. chat_id .. '&message_id='..message_id..'&text=' .. URL.escape(text) 111 | 112 | if markdown then 113 | url = url .. '&parse_mode=Markdown' 114 | end 115 | 116 | url = url .. '&disable_web_page_preview=true' 117 | 118 | if keyboard then 119 | url = url..'&reply_markup='..JSON.encode(keyboard) 120 | end 121 | 122 | return send_req(url) 123 | 124 | end 125 | 126 | function edit_inline(inline_message_id, text, keyboard) 127 | local urlk = send_api .. '/editMessageText?inline_message_id='..inline_message_id..'&text=' .. URL.escape(text) 128 | urlk = urlk .. '&parse_mode=Markdown' 129 | if keyboard then 130 | urlk = urlk..'&reply_markup='..URL.escape(json:encode(keyboard)) 131 | end 132 | return send_req(urlk) 133 | end 134 | 135 | function get_alert(callback_query_id, text, show_alert) 136 | 137 | local url = send_api .. '/answerCallbackQuery?callback_query_id=' .. callback_query_id .. '&text=' .. URL.escape(text) 138 | 139 | if show_alert then 140 | url = url..'&show_alert=true' 141 | end 142 | 143 | return send_req(url) 144 | 145 | end 146 | 147 | function send_inline(inline_query_id, query_id , title , description , text , keyboard) 148 | local results = {{}} 149 | results[1].id = query_id 150 | results[1].type = 'article' 151 | results[1].description = description 152 | results[1].title = title 153 | results[1].message_text = text 154 | url = send_api .. '/answerInlineQuery?inline_query_id=' .. inline_query_id ..'&results=' .. URL.escape(json:encode(results))..'&parse_mode=Markdown&cache_time=' .. 1 155 | url = send_api .. '&parse_mode=Markdown' 156 | if keyboard then 157 | results[1].reply_markup = keyboard 158 | url = send_api .. '/answerInlineQuery?inline_query_id=' .. inline_query_id ..'&results=' .. URL.escape(json:encode(results))..'&parse_mode=Markdown&cache_time=' .. 1 159 | end 160 | return send_req(url) 161 | end 162 | 163 | function edit_key(chat_id, message_id, reply_markup) 164 | 165 | local url = send_api .. '/editMessageReplyMarkup?chat_id=' .. chat_id .. 166 | '&message_id='..message_id.. 167 | '&reply_markup='..URL.escape(JSON.encode(reply_markup)) 168 | 169 | return send_req(url) 170 | 171 | end 172 | 173 | function string:input() 174 | if not self:find(' ') then 175 | return false 176 | end 177 | return self:sub(self:find(' ')+1) 178 | end 179 | 180 | function serialize_to_file(data, file, uglify) 181 | file = io.open(file, 'w+') 182 | local serialized 183 | if not uglify then 184 | serialized = serpent.block(data, { 185 | comment = false, 186 | name = '_' 187 | }) 188 | else 189 | serialized = serpent.dump(data) 190 | end 191 | file:write(serialized) 192 | file:close() 193 | end 194 | 195 | function is_sudo(msg) 196 | local var = false 197 | -- Check users id in config 198 | for v,user in pairs(_config.sudo_users) do 199 | if user == msg.from.id then 200 | var = true 201 | end 202 | end 203 | return var 204 | end 205 | 206 | function getChatMember(chat_id, user_id) 207 | local url = send_api .. '/getChatMember?chat_id=' .. chat_id .. '&user_id=' .. user_id 208 | return send_req(url) 209 | end 210 | 211 | function getChatAdministrators(chat_id) 212 | local url = send_api .. '/getChatAdministrators?chat_id=' .. chat_id 213 | return send_req(url) 214 | end 215 | 216 | function unbanChatMember(chat_id, user_id) 217 | local url = send_api .. '/unbanChatMember?chat_id=' .. chat_id .. '&user_id=' .. user_id 218 | return send_req(url) 219 | end 220 | 221 | function leave_group(chat_id) 222 | local url = send_api .. '/leaveChat?chat_id=' .. chat_id 223 | return send_req(url) 224 | end 225 | 226 | function del_msg(chat_id, message_id) 227 | local url = send_api..'/deletemessage?chat_id='..chat_id..'&message_id='..message_id 228 | return send_req(url) 229 | end 230 | 231 | function kick_user(user_id, chat_id) 232 | local url = send_api .. '/kickChatMember?chat_id=' .. chat_id .. '&user_id=' .. user_id 233 | return send_req(url) 234 | end 235 | 236 | function getChatMembersCount(chat_id) 237 | 238 | local url = send_api .. '/getChatMembersCount?chat_id=' .. chat_id 239 | 240 | return send_req(url) 241 | 242 | end 243 | 244 | function downloadFile(file_id, download_path) 245 | 246 | if not file_id then return nil, "file_id not specified" end 247 | if not download_path then return nil, "download_path not specified" end 248 | 249 | local response = {} 250 | 251 | local file_info = getFile(file_id) 252 | local download_file_path = download_path or "downloads/" .. file_info.result.file_path 253 | 254 | local download_file = io.open(download_file_path, "w") 255 | 256 | if not download_file then return nil, "download_file could not be created" 257 | else 258 | local success, code, headers, status = https.request{ 259 | url = "https://api.telegram.org/file/bot" .. bot_token .. "/" .. file_info.result.file_path, 260 | --source = ltn12.source.string(body), 261 | sink = ltn12.sink.file(download_file), 262 | } 263 | 264 | local r = { 265 | success = true, 266 | download_path = download_file_path, 267 | file = file_info.result 268 | } 269 | return r 270 | end 271 | end 272 | 273 | function restrictChatMember(chat_id, user_id, can_send_messages, can_send_media_messages, can_send_other_messages, can_add_web_page_previews, until_date) 274 | 275 | local url = send_api .. '/restrictChatMember?chat_id=' .. chat_id .. '&user_id=' .. user_id 276 | 277 | if can_send_messages then 278 | url = url .. '&can_send_messages=true' 279 | else 280 | url = url .. '&can_send_messages=false' 281 | end 282 | 283 | if can_send_media_messages then 284 | url = url .. '&can_send_media_messages=true' 285 | else 286 | url = url .. '&can_send_media_messages=false' 287 | end 288 | 289 | if can_send_other_messages then 290 | url = url .. '&can_send_other_messages=true' 291 | else 292 | url = url .. '&can_send_other_messages=false' 293 | end 294 | 295 | if can_add_web_page_previews then 296 | url = url .. '&can_add_web_page_previews=true' 297 | else 298 | url = url .. '&can_add_web_page_previews=false' 299 | end 300 | 301 | if until_date then 302 | url = url .. '&until_date='..until_date 303 | end 304 | return send_req(url) 305 | 306 | end 307 | 308 | function restrictChatMember(chat_id, user_id, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members) 309 | 310 | local url = send_api .. '/restrictChatMember?chat_id=' .. chat_id .. '&user_id=' .. user_id 311 | 312 | if can_change_info then 313 | url = url .. '&can_change_info=true' 314 | else 315 | url = url .. '&can_change_info=false' 316 | end 317 | 318 | if can_post_messages then 319 | url = url .. '&can_post_messages=true' 320 | else 321 | url = url .. '&can_post_messages=false' 322 | end 323 | 324 | if can_edit_messages then 325 | url = url .. '&can_edit_messages=true' 326 | else 327 | url = url .. '&can_edit_messages=false' 328 | end 329 | 330 | if can_delete_messages then 331 | url = url .. '&can_delete_messages=true' 332 | else 333 | url = url .. '&can_delete_messages=false' 334 | end 335 | 336 | if can_invite_users then 337 | url = url .. '&can_invite_users=true' 338 | else 339 | url = url .. '&can_invite_users=false' 340 | end 341 | 342 | if can_restrict_members then 343 | url = url .. '&can_restrict_members=true' 344 | else 345 | url = url .. '&can_restrict_members=false' 346 | end 347 | 348 | if can_pin_messages then 349 | url = url .. '&can_pin_messages=true' 350 | else 351 | url = url .. '&can_pin_messages=false' 352 | end 353 | 354 | if can_promote_members then 355 | url = url .. '&can_promote_members=true' 356 | else 357 | url = url .. '&can_promote_members=false' 358 | end 359 | return send_req(url) 360 | 361 | end 362 | 363 | function exportChatInviteLink(chat_id) 364 | 365 | local url = send_api .. '/exportChatInviteLink?chat_id=' .. chat_id 366 | return send_req(url) 367 | 368 | end 369 | 370 | function setChatPhoto(chat_id, photo) 371 | 372 | local url = send_api .. '/setChatPhoto' 373 | cUrl_Command:setopt_url(url) 374 | 375 | local form = curl.form() 376 | form:add_content("chat_id", chat_id) 377 | form:add_file("photo", photo) 378 | data = {} 379 | local c = cUrl_Command:setopt_writefunction(table.insert, data) 380 | :setopt_httppost(form) 381 | :perform() 382 | return table.concat(data), c:getinfo_response_code() 383 | 384 | end 385 | 386 | function deleteChatPhoto(chat_id) 387 | 388 | local url = send_api .. '/deleteChatPhoto?chat_id=' .. chat_id 389 | return send_req(url) 390 | 391 | end 392 | 393 | function setChatTitle(chat_id, title) 394 | 395 | local url = send_api .. '/setChatTitle?chat_id=' .. chat_id .. '&title=' .. URL.escape(title) 396 | return send_req(url) 397 | 398 | end 399 | 400 | function setChatDescription(chat_id, description) 401 | 402 | local url = send_api .. '/setChatDescription?chat_id=' .. chat_id .. '&description=' .. URL.escape(description) 403 | return send_req(url) 404 | 405 | end 406 | 407 | function pinChatMessage(chat_id, message_id, disable_notification) 408 | 409 | local url = send_api .. '/pinChatMessage?chat_id=' .. chat_id .. '&message_id=' .. message_id 410 | if disable_notification then 411 | url = url .. '&disable_notification=false' 412 | end 413 | return send_req(url) 414 | 415 | end 416 | 417 | function unpinChatMessage(chat_id) 418 | 419 | local url = send_api .. '/unpinChatMessage?chat_id=' .. chat_id 420 | return send_req(url) 421 | 422 | end 423 | 424 | function sendPhotoById(chat_id, file_id, reply_to_message_id, caption) 425 | 426 | local url = send_api .. '/sendPhoto?chat_id=' .. chat_id .. '&photo=' .. file_id 427 | 428 | if reply_to_message_id then 429 | url = url..'&reply_to_message_id='..reply_to_message_id 430 | end 431 | if caption then 432 | url = url..'&caption='..URL.escape(caption) 433 | end 434 | 435 | return send_req(url) 436 | 437 | end 438 | 439 | function sendPhoto(chat_id, photo, caption, reply_to_message_id, reply_markup, disable_notification) 440 | 441 | local url = send_api .. '/sendPhoto' 442 | cUrl_Command:setopt_url(url) 443 | 444 | local form = curl.form() 445 | form:add_content("chat_id", chat_id) 446 | form:add_file("photo", photo) 447 | 448 | if reply_to_message_id then 449 | form:add_content("reply_to_message_id", reply_to_message_id) 450 | end 451 | 452 | if caption then 453 | form:add_content("caption", caption) 454 | end 455 | 456 | if reply_markup then 457 | form:add_content("reply_markup", URL.escape(JSON.encode(reply_markup))) 458 | end 459 | 460 | if disable_notification then 461 | form:add_content("disable_notification", 'false') 462 | end 463 | 464 | data = {} 465 | 466 | local c = cUrl_Command:setopt_writefunction(table.insert, data) 467 | :setopt_httppost(form) 468 | :perform() 469 | 470 | return table.concat(data), c:getinfo_response_code() 471 | end 472 | 473 | function sendAudio(chat_id, audio, reply_to_message_id, caption, title, duration, performer, reply_markup, disable_notification) 474 | 475 | local url = send_api .. '/sendAudio' 476 | cUrl_Command:setopt_url(url) 477 | 478 | local form = curl.form() 479 | form:add_content("chat_id", chat_id) 480 | form:add_file("audio", audio) 481 | 482 | if reply_to_message_id then 483 | form:add_content("reply_to_message_id", reply_to_message_id) 484 | end 485 | 486 | if caption then 487 | form:add_content("caption", caption) 488 | end 489 | 490 | if duration then 491 | form:add_content("duration", duration) 492 | end 493 | 494 | if performer then 495 | form:add_content("performer", performer) 496 | end 497 | 498 | if title then 499 | form:add_content("title", title) 500 | end 501 | 502 | if reply_markup then 503 | form:add_content("reply_markup", URL.escape(JSON.encode(reply_markup))) 504 | end 505 | 506 | if disable_notification then 507 | form:add_content("disable_notification", 'false') 508 | end 509 | 510 | data = {} 511 | 512 | local c = cUrl_Command:setopt_writefunction(table.insert, data) 513 | :setopt_httppost(form) 514 | :perform() 515 | 516 | return table.concat(data), c:getinfo_response_code() 517 | 518 | end 519 | 520 | function sendDocument(chat_id, document, reply_to_message_id, caption, reply_markup, disable_notification) 521 | 522 | local url = send_api .. '/sendDocument' 523 | cUrl_Command:setopt_url(url) 524 | 525 | local form = curl.form() 526 | form:add_content("chat_id", chat_id) 527 | form:add_file("document", document) 528 | 529 | if reply_to_message_id then 530 | form:add_content("reply_to_message_id", reply_to_message_id) 531 | end 532 | 533 | if caption then 534 | form:add_content("caption", caption) 535 | end 536 | 537 | if reply_markup then 538 | form:add_content("reply_markup", URL.escape(JSON.encode(reply_markup))) 539 | end 540 | 541 | if disable_notification then 542 | form:add_content("disable_notification", 'false') 543 | end 544 | 545 | data = {} 546 | 547 | local c = cUrl_Command:setopt_writefunction(table.insert, data) 548 | :setopt_httppost(form) 549 | :perform() 550 | 551 | return table.concat(data), c:getinfo_response_code() 552 | 553 | end 554 | 555 | function sendSticker(chat_id, sticker, reply_to_message_id, reply_markup, disable_notification) 556 | 557 | local url = send_api .. '/sendSticker' 558 | cUrl_Command:setopt_url(url) 559 | 560 | local form = curl.form() 561 | form:add_content("chat_id", chat_id) 562 | form:add_file("sticker", sticker) 563 | 564 | if reply_to_message_id then 565 | form:add_content("reply_to_message_id", reply_to_message_id) 566 | end 567 | 568 | if reply_markup then 569 | form:add_content("reply_markup", URL.escape(JSON.encode(reply_markup))) 570 | end 571 | 572 | if disable_notification then 573 | form:add_content("disable_notification", 'false') 574 | end 575 | 576 | data = {} 577 | 578 | local c = cUrl_Command:setopt_writefunction(table.insert, data) 579 | :setopt_httppost(form) 580 | :perform() 581 | 582 | return table.concat(data), c:getinfo_response_code() 583 | 584 | end 585 | 586 | function sendVideo(chat_id, video, duration, caption, reply_to_message_id, reply_markup, width, height, disable_notification) 587 | 588 | local url = send_api .. '/sendVideo' 589 | cUrl_Command:setopt_url(url) 590 | 591 | local form = curl.form() 592 | form:add_content("chat_id", chat_id) 593 | form:add_file("video", video) 594 | 595 | if reply_to_message_id then 596 | form:add_content("reply_to_message_id", reply_to_message_id) 597 | end 598 | 599 | if duration then 600 | form:add_content("duration", duration) 601 | end 602 | 603 | if caption then 604 | form:add_content("caption", caption) 605 | end 606 | 607 | if reply_markup then 608 | form:add_content("reply_markup", URL.escape(JSON.encode(reply_markup))) 609 | end 610 | 611 | if width then 612 | form:add_content("width", width) 613 | end 614 | 615 | if height then 616 | form:add_content("height", height) 617 | end 618 | if disable_notification then 619 | form:add_content("disable_notification", 'false') 620 | end 621 | data = {} 622 | 623 | local c = cUrl_Command:setopt_writefunction(table.insert, data) 624 | :setopt_httppost(form) 625 | :perform() 626 | 627 | return table.concat(data), c:getinfo_response_code() 628 | 629 | end 630 | 631 | function sendVoice(chat_id, voice, reply_to_message_id, caption, duration, reply_markup, disable_notification) 632 | 633 | local url = send_api .. '/sendVoice' 634 | cUrl_Command:setopt_url(url) 635 | 636 | local form = curl.form() 637 | form:add_content("chat_id", chat_id) 638 | form:add_file("voice", voice) 639 | 640 | if reply_to_message_id then 641 | form:add_content("reply_to_message_id", reply_to_message_id) 642 | end 643 | 644 | if caption then 645 | form:add_content("caption", caption) 646 | end 647 | 648 | if duration then 649 | form:add_content("duration", duration) 650 | end 651 | 652 | if reply_markup then 653 | form:add_content("reply_markup", URL.escape(JSON.encode(reply_markup))) 654 | end 655 | if disable_notification then 656 | form:add_content("disable_notification", 'false') 657 | end 658 | data = {} 659 | 660 | local c = cUrl_Command:setopt_writefunction(table.insert, data) 661 | :setopt_httppost(form) 662 | :perform() 663 | 664 | return table.concat(data), c:getinfo_response_code() 665 | 666 | end 667 | 668 | function sendLocation(chat_id, latitude, longitude, reply_to_message_id, reply_markup, disable_notification) 669 | 670 | local url = send_api .. '/sendLocation?chat_id=' .. chat_id .. '&latitude=' .. latitude .. '&longitude=' .. longitude 671 | 672 | if reply_to_message_id then 673 | url = url .. '&reply_to_message_id=' .. reply_to_message_id 674 | end 675 | 676 | if disable_notification then 677 | url = url .. '&disable_notification=false' 678 | end 679 | 680 | if reply_markup then 681 | url = url..'&reply_markup='..URL.escape(JSON.encode(reply_markup)) 682 | end 683 | 684 | return send_req(url) 685 | 686 | end 687 | 688 | function sendContact(chat_id, phone_number, first_name, last_name, reply_to_message_id, reply_markup, disable_notification) 689 | 690 | local url = send_api.. '/sendContact?chat_id=' .. chat_id .. '&phone_number=' .. phone_number .. '&first_name=' .. first_name 691 | 692 | if last_name then 693 | url = url .. '&last_name=' .. last_name 694 | end 695 | 696 | if reply_to_message_id then 697 | url = url .. '&reply_to_message_id=' .. reply_to_message_id 698 | end 699 | 700 | if disable_notification then 701 | url = url .. '&disable_notification=false' 702 | end 703 | 704 | if reply_markup then 705 | url = url..'&reply_markup='..URL.escape(JSON.encode(reply_markup)) 706 | end 707 | 708 | return send_req(url) 709 | 710 | end 711 | 712 | -- action = typing, upload_photo, record_video, upload_video, record_audio, upload_audio, upload_document, find_location 713 | function sendChatAction(chat_id, action) 714 | 715 | local url = send_api .. '/sendChatAction?chat_id=' .. chat_id .. '&action=' .. action 716 | return send_req(url) 717 | 718 | end 719 | 720 | function getUserProfilePhotos(user_id, offset, limit) 721 | 722 | local url = send_api .. '/getUserProfilePhotos?user_id='..user_id 723 | if offset then 724 | url = url..'&offset='..offset 725 | end 726 | if limit then 727 | if tonumber(limit) > 100 then 728 | limit = 100 729 | end 730 | url = url..'&limit='..limit 731 | end 732 | return send_req(url) 733 | 734 | end 735 | 736 | function getFile(file_id) 737 | 738 | local url = send_api .. '/getFile?file_id='..file_id 739 | return send_req(url) 740 | 741 | end 742 | -------------------------------------------------------------------------------- /plugins/tools.lua: -------------------------------------------------------------------------------- 1 | -- @BeyondTeam 2 | local function getindex(t,id) 3 | for i,v in pairs(t) do 4 | if v == id then 5 | return i 6 | end 7 | end 8 | return nil 9 | end 10 | 11 | local function index_function(user_id) 12 | for k,v in pairs(_config.admins) do 13 | if user_id == v[1] then 14 | print(k) 15 | return k 16 | end 17 | end 18 | -- If not found 19 | return false 20 | end 21 | 22 | local function reload_plugins( ) 23 | plugins = {} 24 | load_plugins() 25 | end 26 | 27 | --By @SoLiD021 28 | local function already_sudo(user_id) 29 | for k,v in pairs(_config.sudo_users) do 30 | if user_id == v then 31 | return k 32 | end 33 | end 34 | -- If not found 35 | return false 36 | end 37 | 38 | --By @SoLiD021 39 | local function already_admin(user_id) 40 | for k,v in pairs(_config.admins) do 41 | if user_id == v[1] then 42 | print(k) 43 | return k 44 | end 45 | end 46 | -- If not found 47 | return false 48 | end 49 | 50 | --By @SoLiD 51 | local function sudolist(msg) 52 | local hash = "group_lang:"..msg.to.id 53 | local lang = redis:get(hash) 54 | local sudo_users = _config.sudo_users 55 | if not lang then 56 | text = "*List of sudo users :*\n" 57 | else 58 | text = "_لیست سودو های ربات :_\n" 59 | end 60 | for i=1,#sudo_users do 61 | text = text..i.." - "..sudo_users[i].."\n" 62 | end 63 | return text 64 | end 65 | 66 | local function adminlist(msg) 67 | local hash = "group_lang:"..msg.to.id 68 | local lang = redis:get(hash) 69 | if not lang then 70 | text = "*List of bot admins :*\n" 71 | else 72 | text = "_لیست ادمین های ربات :_\n" 73 | end 74 | local compare = text 75 | local i = 1 76 | for v,user in pairs(_config.admins) do 77 | text = text..i..'- '..(user[2] or '')..' ➣ ('..user[1]..')\n' 78 | i = i +1 79 | end 80 | if compare == text then 81 | if not lang then 82 | text = '_No_ *admins* _available_' 83 | else 84 | text = '_هیچ ادمینی برای ربات تعیین نشده_' 85 | end 86 | end 87 | return text 88 | end 89 | 90 | local function chat_list(msg) 91 | local hash = "group_lang:"..msg.to.id 92 | local lang = redis:get(hash) 93 | i = 1 94 | local data = load_data(_config.moderation.data) 95 | local groups = 'groups' 96 | if not data[tostring(groups)] then 97 | if not lang then 98 | return '_No_ *groups* _at the moment_' 99 | else 100 | return '_هیچ گروهی ثبت نشده_' 101 | end 102 | end 103 | if not lang then 104 | message = '*List of Groups:*\n\n' 105 | else 106 | message = '_لیست گروه های ربات:_\n\n' 107 | end 108 | for k,v in pairsByKeys(data[tostring(groups)]) do 109 | local group_id = v 110 | if data[tostring(group_id)] then 111 | settings = data[tostring(group_id)]['settings'] 112 | end 113 | for m,n in pairsByKeys(settings) do 114 | if m == 'set_name' then 115 | name = n:gsub("", "") 116 | chat_name = name:gsub("‮", "") 117 | group_name_id = name .. '\n(ID: ' ..group_id.. ')\n\n' 118 | if name:match("[\216-\219][\128-\191]") then 119 | group_info = i..' - \n'..group_name_id 120 | else 121 | group_info = i..' - '..group_name_id 122 | end 123 | i = i + 1 124 | end 125 | end 126 | message = message..group_info 127 | end 128 | return message 129 | end 130 | 131 | local function run(msg, matches) 132 | local hash = "group_lang:"..msg.to.id 133 | local lang = redis:get(hash) 134 | local data = load_data(_config.moderation.data) 135 | if matches[1] == "sudolist" and is_sudo(msg) then 136 | return sudolist(msg) 137 | end 138 | if tonumber(msg.from.id) == tonumber(sudo_id) then 139 | if matches[1] == "visudo" then 140 | if not matches[2] and msg.reply_to_message then 141 | if msg.reply.username then 142 | username = "@"..check_markdown(msg.reply.username) 143 | else 144 | username = escape_markdown(msg.reply.print_name) 145 | end 146 | if already_sudo(tonumber(msg.reply.id)) then 147 | if not lang then 148 | return "_User_ "..username.." `"..msg.reply.id.."` _is already_ *sudoer*" 149 | else 150 | return "_کاربر_ "..username.." `"..msg.reply.id.."` _از قبل سودو ربات بود_" 151 | end 152 | else 153 | table.insert(_config.sudo_users, tonumber(msg.reply.id)) 154 | print(msg.reply.id..' added to sudo users') 155 | save_config() 156 | reload_plugins(true) 157 | if not lang then 158 | return "_User_ "..username.." `"..msg.reply.id.."` _is now_ *sudoer*" 159 | else 160 | return "_کاربر_ "..username.." `"..msg.reply.id.."` _به مقام سودو ربات منتصب شد_" 161 | end 162 | end 163 | elseif matches[2] and matches[2]:match('^%d+') then 164 | if not getUser(matches[2]).result then 165 | return "*User not found*" 166 | end 167 | local user_name = '@'..check_markdown(getUser(matches[2]).information.username) 168 | if not user_name then 169 | user_name = escape_markdown(getUser(matches[2]).information.first_name) 170 | end 171 | if already_sudo(tonumber(matches[2])) then 172 | if not lang then 173 | return "_User_ "..user_name.." `"..matches[2].."` _is already_ *sudoer*" 174 | else 175 | return "_کاربر_ "..user_name.." `"..matches[2].."` _از قبل سودو ربات بود_" 176 | end 177 | else 178 | table.insert(_config.sudo_users, tonumber(matches[2])) 179 | print(matches[2]..' added to sudo users') 180 | save_config() 181 | reload_plugins(true) 182 | if not lang then 183 | return "_User_ "..user_name.." `"..matches[2].."` _is now_ *sudoer*" 184 | else 185 | return "_کاربر_ "..user_name.." `"..matches[2].."` _به مقام سودو ربات منتصب شد_" 186 | end 187 | end 188 | elseif matches[2] and not matches[2]:match('^%d+') then 189 | if not resolve_username(matches[2]).result then 190 | return "*User not found*" 191 | end 192 | local status = resolve_username(matches[2]) 193 | if already_sudo(tonumber(status.information.id)) then 194 | if not lang then 195 | return "_User_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _is already_ *sudoer*" 196 | else 197 | return "_کاربر_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _از قبل سودو ربات بود_" 198 | end 199 | else 200 | table.insert(_config.sudo_users, tonumber(status.information.id)) 201 | print(status.information.id..' added to sudo users') 202 | save_config() 203 | reload_plugins(true) 204 | if not lang then 205 | return "_User_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _is now_ *sudoer*" 206 | else 207 | return "_کاربر_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _به مقام سودو ربات منتصب شد_" 208 | end 209 | end 210 | end 211 | end 212 | if matches[1] == "desudo" then 213 | if not matches[2] and msg.reply_to_message then 214 | if msg.reply.username then 215 | username = "@"..check_markdown(msg.reply.username) 216 | else 217 | username = escape_markdown(msg.reply.print_name) 218 | end 219 | if not already_sudo(tonumber(msg.reply.id)) then 220 | if not lang then 221 | return "_User_ "..username.." `"..msg.reply.id.."` _is not_ *sudoer*" 222 | else 223 | return "_کاربر_ "..username.." `"..msg.reply.id.."` _از قبل سودو ربات نبود_" 224 | end 225 | else 226 | table.remove(_config.sudo_users, getindex( _config.sudo_users, tonumber(msg.reply.id))) 227 | save_config() 228 | reload_plugins(true) 229 | if not lang then 230 | return "_User_ "..username.." `"..msg.reply.id.."` _is no longer_ *sudoer*" 231 | else 232 | return "_کاربر_ "..username.." `"..msg.reply.id.."` _از مقام سودو ربات برکنار شد_" 233 | end 234 | end 235 | elseif matches[2] and matches[2]:match('^%d+') then 236 | if not getUser(matches[2]).result then 237 | return "*User not found*" 238 | end 239 | local user_name = '@'..check_markdown(getUser(matches[2]).information.username) 240 | if not user_name then 241 | user_name = escape_markdown(getUser(matches[2]).information.first_name) 242 | end 243 | if not already_sudo(tonumber(matches[2])) then 244 | if not lang then 245 | return "_User_ "..user_name.." `"..matches[2].."` _is not_ *sudoer*" 246 | else 247 | return "_کاربر_ "..user_name.." `"..matches[2].."` _از قبل سودو ربات نبود_" 248 | end 249 | else 250 | table.remove(_config.sudo_users, getindex( _config.sudo_users, tonumber(matches[2]))) 251 | save_config() 252 | reload_plugins(true) 253 | if not lang then 254 | return "_User_ "..user_name.." `"..matches[2].."` _is no longer_ *sudoer*" 255 | else 256 | return "_کاربر_ "..user_name.." `"..matches[2].."` _از مقام سودو ربات برکنار شد_" 257 | end 258 | end 259 | elseif matches[2] and not matches[2]:match('^%d+') then 260 | if not resolve_username(matches[2]).result then 261 | return "*User not found*" 262 | end 263 | local status = resolve_username(matches[2]) 264 | if not already_sudo(tonumber(status.information.id)) then 265 | if not lang then 266 | return "_User_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _is not_ *sudoer*" 267 | else 268 | return "_کاربر_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _از قبل سودو ربات نبود_" 269 | end 270 | else 271 | table.remove(_config.sudo_users, getindex( _config.sudo_users, tonumber(status.information.id))) 272 | save_config() 273 | reload_plugins(true) 274 | if not lang then 275 | return "_User_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _is no longer_ *sudoer*" 276 | else 277 | return "_کاربر_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _از مقام سودو ربات برکنار شد_" 278 | end 279 | end 280 | end 281 | end 282 | end 283 | if is_sudo(msg) then 284 | if matches[1] == "adminprom" then 285 | if not matches[2] and msg.reply_to_message then 286 | if msg.reply.username then 287 | username = "@"..check_markdown(msg.reply.username) 288 | else 289 | username = escape_markdown(msg.reply.print_name) 290 | end 291 | if already_admin(tonumber(msg.reply.id)) then 292 | if not lang then 293 | return "_User_ "..username.." `"..msg.reply.id.."` _is already an_ *admin*" 294 | else 295 | return "_کاربر_ "..username.." `"..msg.reply.id.."` _از قبل ادمین ربات بود_" 296 | end 297 | else 298 | table.insert(_config.admins, {tonumber(msg.reply.id), username}) 299 | save_config() 300 | if not lang then 301 | return "_User_ "..username.." `"..msg.reply.id.."` _has been promoted as_ *admin*" 302 | else 303 | return "_کاربر_ "..username.." `"..msg.reply.id.."` _به مقام ادمین ربات منتصب شد_" 304 | end 305 | end 306 | elseif matches[2] and matches[2]:match('^%d+') then 307 | if not getUser(matches[2]).result then 308 | return "*User not found*" 309 | end 310 | local user_name = '@'..check_markdown(getUser(matches[2]).information.username) 311 | if not user_name then 312 | user_name = escape_markdown(getUser(matches[2]).information.first_name) 313 | end 314 | if already_admin(tonumber(matches[2])) then 315 | if not lang then 316 | return "_User_ "..user_name.." `"..matches[2].."` _is already an_ *admin*" 317 | else 318 | return "_کاربر_ "..user_name.." `"..matches[2].."` _از قبل ادمین ربات بود_" 319 | end 320 | else 321 | table.insert(_config.admins, {tonumber(matches[2]), user_name}) 322 | save_config() 323 | if not lang then 324 | return "_User_ "..user_name.." `"..matches[2].."` _has been promoted as_ *admin*" 325 | else 326 | return "_کاربر_ "..user_name.." `"..matches[2].."` _به مقام ادمین ربات منتصب شد_" 327 | end 328 | end 329 | elseif matches[2] and not matches[2]:match('^%d+') then 330 | if not resolve_username(matches[2]).result then 331 | return "*User not found*" 332 | end 333 | local status = resolve_username(matches[2]) 334 | if already_admin(tonumber(status.information.id)) then 335 | if not lang then 336 | return "_User_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _is already an_ *admin*" 337 | else 338 | return "_کاربر_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _از قبل ادمین ربات بود_" 339 | end 340 | else 341 | table.insert(_config.admins, {tonumber(status.information.id), '@'..check_markdown(status.information.username)}) 342 | save_config() 343 | if not lang then 344 | return "_User_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _has been promoted as_ *admin*" 345 | else 346 | return "_کاربر_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _به مقام ادمین ربات منتصب شد_" 347 | end 348 | end 349 | end 350 | end 351 | if matches[1] == "admindem" then 352 | if not matches[2] and msg.reply_to_message then 353 | if msg.reply.username then 354 | username = "@"..check_markdown(msg.reply.username) 355 | else 356 | username = escape_markdown(msg.reply.print_name) 357 | end 358 | if not already_admin(tonumber(msg.reply.id)) then 359 | if not lang then 360 | return "_User_ "..username.." `"..msg.reply.id.."` _is not_ *admin*" 361 | else 362 | return "_کاربر_ "..username.." `"..msg.reply.id.."` _از قبل ادمین ربات نبود_" 363 | end 364 | else 365 | local nameid = index_function(tonumber(msg.reply.id)) 366 | table.remove(_config.admins, nameid) 367 | save_config() 368 | if not lang then 369 | return "_User_ "..username.." `"..msg.reply.id.."` _has been demoted from_ *admin*" 370 | else 371 | return "_کاربر_ "..username.." `"..msg.reply.id.."` _از مقام ادمین ربات برکنار شد_" 372 | end 373 | end 374 | elseif matches[2] and matches[2]:match('^%d+') then 375 | if not getUser(matches[2]).result then 376 | return "*User not found*" 377 | end 378 | local user_name = '@'..check_markdown(getUser(matches[2]).information.username) 379 | if not user_name then 380 | user_name = escape_markdown(getUser(matches[2]).information.first_name) 381 | end 382 | if not already_admin(tonumber(matches[2])) then 383 | if not lang then 384 | return "_User_ "..user_name.." `"..matches[2].."` _is not_ *admin*" 385 | else 386 | return "_کاربر_ "..user_name.." `"..matches[2].."` _از قبل ادمین ربات نبود_" 387 | end 388 | else 389 | local nameid = index_function(tonumber(matches[2])) 390 | table.remove(_config.admins, nameid) 391 | save_config() 392 | if not lang then 393 | return "_User_ "..user_name.." `"..matches[2].."` _has been demoted from_ *admin*" 394 | else 395 | return "_کاربر_ "..user_name.." `"..matches[2].."` _از مقام ادمین ربات برکنار شد_" 396 | end 397 | end 398 | elseif matches[2] and not matches[2]:match('^%d+') then 399 | if not resolve_username(matches[2]).result then 400 | return "*User not found*" 401 | end 402 | local status = resolve_username(matches[2]) 403 | if not already_admin(tonumber(status.information.id)) then 404 | if not lang then 405 | return "_User_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _is not_ *admin*" 406 | else 407 | return "_کاربر_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _از قبل ادمین ربات نبود_" 408 | end 409 | else 410 | local nameid = index_function(tonumber(status.information.id)) 411 | table.remove(_config.admins, nameid) 412 | save_config() 413 | if not lang then 414 | return "_User_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _has been demoted from_ *admin*" 415 | else 416 | return "_کاربر_ @"..check_markdown(status.information.username).." `"..status.information.id.."` _از مقام ادمین ربات برکنار شد_" 417 | end 418 | end 419 | end 420 | end 421 | end 422 | if is_sudo(msg) then 423 | if matches[1]:lower() == "sendfile" and matches[2] and matches[3] then 424 | local send_file = "./"..matches[2].."/"..matches[3] 425 | sendDocument(msg.to.id, send_file, msg.id, "@BeyondTeam") 426 | end 427 | if matches[1]:lower() == "sendplug" and matches[2] then 428 | local plug = "./plugins/"..matches[2]..".lua" 429 | sendDocument(msg.to.id, plug, msg.id, "@BeyondTeam") 430 | end 431 | if matches[1]:lower() == "savefile" and matches[2]then 432 | local fn = matches[2]:gsub('(.*)/', '') 433 | local pt = matches[2]:gsub('/'..fn..'$', '') 434 | if msg.reply_to_message then 435 | if msg.reply_to_message.photo then 436 | if msg.reply_to_message.photo[3] then 437 | fileid = msg.reply_to_message.photo[3].file_id 438 | elseif msg.reply_to_message.photo[2] then 439 | fileid = msg.reply_to_message.photo[2].file_id 440 | else 441 | fileid = msg.reply_to_message.photo[1].file_id 442 | end 443 | elseif msg.reply_to_message.sticker then 444 | fileid = msg.reply_to_message.sticker.file_id 445 | elseif msg.reply_to_message.voice then 446 | fileid = msg.reply_to_message.voice.file_id 447 | elseif msg.reply_to_message.video then 448 | fileid = msg.reply_to_message.video.file_id 449 | elseif msg.reply_to_message.document then 450 | fileid = msg.reply_to_message.document.file_id 451 | end 452 | downloadFile(fileid, "./"..pt.."/"..fn) 453 | if not lang then 454 | return "*File* `"..fn.."` _has been saved in_ *"..pt.."*" 455 | else 456 | return "_فایل_ `"..fn.."` _در پوشه_ *"..pt.."* _ذخیره شد_" 457 | end 458 | end 459 | end 460 | if matches[1]:lower() == "save" and matches[2] then 461 | if msg.reply_to_message then 462 | if msg.reply_to_message.document then 463 | fileid = msg.reply_to_message.document.file_id 464 | filename = msg.reply_to_message.document.file_name 465 | if tostring(filename):match(".lua") then 466 | downloadFile(fileid, "./plugins/"..matches[2]..".lua") 467 | if not lang then 468 | return "*Plugin* `"..matches[2]..".lua` _has been saved_" 469 | else 470 | return "_پلاگین_* `"..matches[2]..".lua` _با موفقیت ذخیره شد_" 471 | end 472 | end 473 | end 474 | end 475 | end 476 | if matches[1] == 'adminlist' and is_admin(msg) then 477 | return adminlist(msg) 478 | end 479 | if matches[1] == 'chats' and is_admin(msg) then 480 | return chat_list(msg) 481 | end 482 | if matches[1] == 'grem' and matches[2] and is_admin(msg) then 483 | local hashgp = "group_lang:"..matches[2] 484 | local langgp = redis:get(hashgp) 485 | local data = load_data(_config.moderation.data) 486 | -- Group configuration removal 487 | data[tostring(matches[2])] = nil 488 | save_data(_config.moderation.data, data) 489 | local groups = 'groups' 490 | if not data[tostring(groups)] then 491 | data[tostring(groups)] = nil 492 | save_data(_config.moderation.data, data) 493 | end 494 | data[tostring(groups)][tostring(matches[2])] = nil 495 | save_data(_config.moderation.data, data) 496 | if not langgp then 497 | send_msg(matches[2], "Group has been removed by admin command", nil, 'md') 498 | else 499 | send_msg(matches[2], "گروه به دستور ادمین و یا سودو ربات از لیست گروه های مدیریتی ربات حذف شد", nil, 'md') 500 | end 501 | if not lang then 502 | return '_Group_ *'..matches[2]..'* _removed_' 503 | else 504 | return '_گروه_ *'..matches[2]..'* _از لیست گروه های مدیریتی ربات حذف شد_' 505 | end 506 | end 507 | if matches[1] == 'leave' and is_admin(msg) then 508 | leave_group(msg.to.id) 509 | end 510 | if matches[1] == 'bc' and is_admin(msg) and matches[2] and matches[3] then 511 | local text = matches[2] 512 | send_msg(matches[3], text) end 513 | end 514 | if matches[1] == 'broadcast' and is_sudo(msg) then 515 | local data = load_data(_config.moderation.data) 516 | local bc = matches[2] 517 | for k,v in pairs(data) do 518 | send_msg(k, bc) 519 | end 520 | end 521 | if matches[1] == 'autoleave' and is_admin(msg) then 522 | local hash = 'AutoLeaveBot' 523 | --Enable Auto Leave 524 | if matches[2] == 'enable' then 525 | redis:del(hash) 526 | return 'Auto leave has been enabled' 527 | --Disable Auto Leave 528 | elseif matches[2] == 'disable' then 529 | redis:set(hash, true) 530 | return 'Auto leave has been disabled' 531 | --Auto Leave Status 532 | elseif matches[2] == 'status' then 533 | if not redis:get(hash) then 534 | return 'Auto leave is enable' 535 | else 536 | return 'Auto leave is disable' 537 | end 538 | end 539 | end 540 | ---------------Help Tools---------------- 541 | if matches[1] == "helptools" and is_admin(msg) then 542 | local text = [[ 543 | 544 | _Sudoer And Admins Beyond Bot Help :_ 545 | 546 | *!visudo* `[username|id|reply]` 547 | _Add Sudo_ 548 | 549 | *!desudo* `[username|id|reply]` 550 | _Demote Sudo_ 551 | 552 | *!sudolist * 553 | _Sudo(s) list_ 554 | 555 | *!adminprom* `[username|id|reply]` 556 | _Add admin for bot_ 557 | 558 | *!admindem* `[username|id|reply]` 559 | _Demote bot admin_ 560 | 561 | *!adminlist * 562 | _Admin(s) list_ 563 | 564 | *!leave * 565 | _Leave current group_ 566 | 567 | *!autoleave* `[disable/enable]` 568 | _Automatically leaves group_ 569 | 570 | *!chats* 571 | _List of added groups_ 572 | 573 | *!grem* `[id]` 574 | _Remove a group from Database_ 575 | 576 | *!broadcast* `[text]` 577 | _Send message to all added groups_ 578 | 579 | *!bc* `[text] [GroupID]` 580 | _Send message to a specific group_ 581 | 582 | *!sendfile* `[folder] [file]` 583 | _Send file from folder_ 584 | 585 | *!sendplug* `[plug]` 586 | _Send plugin_ 587 | 588 | *!save* `[plugin name] [reply]` 589 | _Save plugin by reply_ 590 | 591 | *!savefile* `[address/filename] [reply]` 592 | _Save File by reply to specific folder_ 593 | 594 | _You can use_ *[!/]* _at the beginning of commands._ 595 | 596 | `This help is only for sudoers/bot admins.` 597 | 598 | *This means only the sudoers and its bot admins can use mentioned commands.* 599 | 600 | *Good luck ;)*]] 601 | 602 | local fatext = [[ 603 | 604 | _راهنمای سودو و مدیران ربات بیوند :_ 605 | 606 | *!visudo* `[username|id|reply]` 607 | _ارتقا به مقام سودو_ 608 | 609 | *!desudo* `[username|id|reply]` 610 | _حذف مقام سودو_ 611 | 612 | *!sudolist * 613 | _لیست سودو_ 614 | 615 | *!adminprom* `[username|id|reply]` 616 | _ارتقا به ادمین ربات_ 617 | 618 | *!admindem* `[username|id|reply]` 619 | _حذف ادمین ربات_ 620 | 621 | *!adminlist * 622 | _لیست ادمین_ 623 | 624 | *!leave * 625 | _خروج ربات از گروه فعلی_ 626 | 627 | *!autoleave* `[disable/enable]` 628 | _خروج خودکار_ 629 | 630 | *!chats* 631 | _لیست گروههای ربات_ 632 | 633 | *!grem* `[id]` 634 | _حذف گروه با ایدی از لیست گروههای ربات_ 635 | 636 | *!broadcast* `[text]` 637 | _ارسال پیام همگانی به گروههای ربات_ 638 | 639 | *!bc* `[text] [GroupID]` 640 | _ارسال پیام به گروه مورد نظر_ 641 | 642 | *!sendfile* `[folder] [file]` 643 | _دریافت فابل از پوشه ربات_ 644 | 645 | *!sendplug* `[plug]` 646 | _دریافت پلاگین های ربات_ 647 | 648 | *!save* `[plugin name] [reply]` 649 | _ذخیره پلاگین در پوشه پلاگین ها_ 650 | 651 | *!savefile* `[address/filename] [reply]` 652 | _ذخیره فایل در پوشه های ربات_ 653 | 654 | *شما میتوانید از [/!] در اول دستورات برای اجرای آنها بهره بگیرید* 655 | 656 | _این راهنما فقط برای سودو ها/ادمین های ربات میباشد!_ 657 | 658 | `این به این معناست که فقط سودو ها/ادمین های ربات میتوانند از دستورات بالا استفاده کنند!` 659 | 660 | *موفق باشید ;)*]] 661 | if lang then 662 | return fatext 663 | else 664 | return text 665 | end 666 | end 667 | end 668 | return { 669 | patterns = { 670 | "^[!/](helptools)$", 671 | "^[!/](visudo)$", 672 | "^[!/](desudo)$", 673 | "^[!/](visudo) (.*)$", 674 | "^[!/](desudo) (.*)$", 675 | "^[!/](sudolist)$", 676 | "^[!/](adminprom)$", 677 | "^[!/](admindem)$", 678 | "^[!/](adminprom) (.*)$", 679 | "^[!/](admindem) (.*)$", 680 | "^[!/](adminlist)$", 681 | "^[!/](chats)$", 682 | "^[!/](sendfile) (.*) (.*)$", 683 | "^[!/](savefile) (.*)$", 684 | "^[!/](bc) (.*) (-%d+)$", 685 | "^[!/](broadcast) (.*)$", 686 | "^[!/](sendplug) (.*)$", 687 | "^[!/](save) (.*)$", 688 | "^[!/](leave)$", 689 | "^[!/](autoleave) (.*)$", 690 | "^[!/](grem) (-%d+)$", 691 | }, 692 | run = run, 693 | pre_process = pre_process 694 | } 695 | 696 | -- @BeyondTeam 697 | -------------------------------------------------------------------------------- /plugins/banhammer.lua: -------------------------------------------------------------------------------- 1 | -- @BeyondTeam 2 | local function BeyondTeam(msg, matches) 3 | local hash = "group_lang:"..msg.to.id 4 | local lang = redis:get(hash) 5 | local data = load_data(_config.moderation.data) 6 | ----------------Kick ---------------- 7 | if matches[1] == 'kick' and is_mod(msg) then 8 | if msg.reply_id then 9 | if tonumber(msg.reply.id) == tonumber(our_id) then 10 | if not lang then 11 | return "*I can't kick my self*" 12 | else 13 | return "_من قادر به اخراج کردن خود نیستم_" 14 | end 15 | end 16 | if is_mod1(msg.to.id, msg.reply.id) then 17 | if not lang then 18 | return "_You can't_ *kick* _mods, owners, bot admins_" 19 | else 20 | return "_شما نمیتوانید مدیران،صاحبان گروه و ادمین های ربات را اخراج کنید_" 21 | end 22 | else 23 | kick_user(msg.reply.id, msg.to.id) 24 | end 25 | elseif matches[2] and not string.match(matches[2], '^%d+$') then 26 | if not resolve_username(matches[2]).result then 27 | if not lang then 28 | return "*User not found*" 29 | else 30 | return "_کاربر یافت نشد_" 31 | end 32 | end 33 | local User = resolve_username(matches[2]).information 34 | if tonumber(User.id) == tonumber(our_id) then 35 | if not lang then 36 | return "*I can't kick my self*" 37 | else 38 | return "_من قادر به اخراج کردن خود نیستم_" 39 | end 40 | end 41 | if is_mod1(msg.to.id, User.id) then 42 | if not lang then 43 | return "_You can't_ *kick* _mods, owners, bot admins_" 44 | else 45 | return "_شما نمیتوانید مدیران،صاحبان گروه و ادمین های ربات را اخراج کنید_" 46 | end 47 | else 48 | kick_user(User.id, msg.to.id) 49 | end 50 | elseif matches[2] and string.match(matches[2], '^%d+$') then 51 | if tonumber(matches[2]) == tonumber(our_id) then 52 | if not lang then 53 | return "*I can't kick my self*" 54 | else 55 | return "_من قادر به اخراج کردن خود نیستم_" 56 | end 57 | end 58 | if is_mod1(msg.to.id, tonumber(matches[2])) then 59 | if not lang then 60 | return "_You can't_ *kick* _mods, owners, bot admins_" 61 | else 62 | return "_شما نمیتوانید مدیران،صاحبان گروه و ادمین های ربات را اخراج کنید_" 63 | end 64 | else 65 | kick_user(tonumber(matches[2]), msg.to.id) 66 | end 67 | end 68 | end 69 | 70 | ---------------Ban------------------- 71 | 72 | if matches[1] == 'ban' and is_mod(msg) then 73 | if msg.reply_id then 74 | if msg.reply.username then 75 | un = "@"..check_markdown(msg.reply.username) 76 | else 77 | un = escape_markdown(msg.reply.print_name) 78 | end 79 | if tonumber(msg.reply.id) == tonumber(our_id) then 80 | if not lang then 81 | return "*I can't ban my self*" 82 | else 83 | return "_من قادر به محروم کردن خود از گروه نیستم_" 84 | end 85 | end 86 | if is_mod1(msg.to.id, msg.reply.id) then 87 | if not lang then 88 | return "_You can't_ *ban* _mods, owners, bot admins_" 89 | else 90 | return "_شما نمیتوانید مدیران،صاحبان گروه و ادمین های ربات را از گروه محروم کنید_" 91 | end 92 | end 93 | if is_banned(msg.reply.id, msg.to.id) then 94 | if not lang then 95 | return "_User_ "..un.." `"..msg.reply.id.."` _is already_ *banned*" 96 | else 97 | return "_کاربر_ "..un.." `"..msg.reply.id.."` _از گروه محروم بود_" 98 | end 99 | else 100 | ban_user(un, msg.reply.id, msg.to.id) 101 | kick_user(msg.reply.id, msg.to.id) 102 | if not lang then 103 | return "_User_ "..un.." `"..msg.reply.id.."` _has been_ *banned*" 104 | else 105 | return "_کاربر_ "..un.." `"..msg.reply.id.."` _از گروه محروم شد_" 106 | end 107 | end 108 | elseif matches[2] and not string.match(matches[2], '^%d+$') then 109 | if not resolve_username(matches[2]).result then 110 | if not lang then 111 | return "*User not found*" 112 | else 113 | return "_کاربر یافت نشد_" 114 | end 115 | end 116 | local User = resolve_username(matches[2]).information 117 | if tonumber(User.id) == tonumber(our_id) then 118 | if not lang then 119 | return "*I can't ban my self*" 120 | else 121 | return "_من قادر به محروم کردن خود از گروه نیستم_" 122 | end 123 | end 124 | if is_mod1(msg.to.id, User.id) then 125 | if not lang then 126 | return "_You can't_ *ban* _mods, owners, bot admins_" 127 | else 128 | return "_شما نمیتوانید مدیران،صاحبان گروه و ادمین های ربات را از گروه محروم کنید_" 129 | end 130 | end 131 | if is_banned(User.id, msg.to.id) then 132 | if not lang then 133 | return "_User_ @"..check_markdown(User.username).." `"..User.id.."` _is already_ *banned*" 134 | else 135 | return "_کاربر_ @"..check_markdown(User.username).." `"..User.id.."` _از گروه محروم بود_" 136 | end 137 | else 138 | ban_user(check_markdown(User.username), User.id, msg.to.id) 139 | kick_user(User.id, msg.to.id) 140 | if not lang then 141 | return "_User_ @"..check_markdown(User.username).." `"..User.id.."` _has been_ *banned*" 142 | else 143 | return "_کاربر_ @"..check_markdown(User.username).." `"..User.id.."` _از گروه محروم شد_" 144 | end 145 | end 146 | elseif matches[2] and string.match(matches[2], '^%d+$') then 147 | if tonumber(matches[2]) == tonumber(our_id) then 148 | if not lang then 149 | return "*I can't ban my self*" 150 | else 151 | return "_من قادر به محروم کردن خود از گروه نیستم_" 152 | end 153 | end 154 | if is_mod1(msg.to.id, tonumber(matches[2])) then 155 | if not lang then 156 | return "_You can't_ *ban* _mods, owners, bot admins_" 157 | else 158 | return "_شما نمیتوانید مدیران،صاحبان گروه و ادمین های ربات را از گروه محروم کنید_" 159 | end 160 | end 161 | if is_banned(tonumber(matches[2]), msg.to.id) then 162 | if not lang then 163 | return "_User_ `"..matches[2].."` _is already_ *banned*" 164 | else 165 | return "_کاربر_ `"..matches[2].."` _از گروه محروم بود_" 166 | end 167 | else 168 | ban_user('', matches[2], msg.to.id) 169 | kick_user(tonumber(matches[2]), msg.to.id) 170 | if not lang then 171 | return "_User_ `"..matches[2].."` _has been_ *banned*" 172 | else 173 | return "_کاربر_ `"..matches[2].."` _از گروه محروم شد_" 174 | end 175 | end 176 | end 177 | end 178 | 179 | ---------------Unban------------------- 180 | 181 | if matches[1] == 'unban' and is_mod(msg) then 182 | if msg.reply_id then 183 | if msg.reply.username then 184 | un = "@"..check_markdown(msg.reply.username) 185 | else 186 | un = escape_markdown(msg.reply.print_name) 187 | end 188 | if not is_banned(msg.reply.id, msg.to.id) then 189 | if not lang then 190 | return "_User_ "..un.." `"..msg.reply.id.."` _is not_ *banned*" 191 | else 192 | return "_کاربر_ "..un.." `"..msg.reply.id.."` _از گروه محروم نبود_" 193 | end 194 | else 195 | unban_user(msg.reply.id, msg.to.id) 196 | if not lang then 197 | return "_User_ "..un.." `"..msg.reply.id.."` _has been_ *unbanned*" 198 | else 199 | return "_کاربر_ "..un.." `"..msg.reply.id.."` _از محرومیت گروه خارج شد_" 200 | end 201 | end 202 | elseif matches[2] and not string.match(matches[2], '^%d+$') then 203 | if not resolve_username(matches[2]).result then 204 | if not lang then 205 | return "*User not found*" 206 | else 207 | return "_کاربر یافت نشد_" 208 | end 209 | end 210 | local User = resolve_username(matches[2]).information 211 | if not is_banned(User.id, msg.to.id) then 212 | if not lang then 213 | return "_User_ @"..check_markdown(User.username).." `"..User.id.."` _is not_ *banned*" 214 | else 215 | return "_کاربر_ @"..check_markdown(User.username).." `"..User.id.."` _از گروه محروم نبود_" 216 | end 217 | else 218 | unban_user(User.id, msg.to.id) 219 | if not lang then 220 | return "_User_ @"..check_markdown(User.username).." `"..User.id.."` _has been_ *unbanned*" 221 | else 222 | return "_کاربر_ @"..check_markdown(User.username).." `"..User.id.."` _از محرومیت گروه خارج شد_" 223 | end 224 | end 225 | elseif matches[2] and string.match(matches[2], '^%d+$') then 226 | if not is_banned(tonumber(matches[2]), msg.to.id) then 227 | if not lang then 228 | return "_User_ `"..matches[2].."` _is not_ *banned*" 229 | else 230 | return "_کاربر_ `"..matches[2].."` _از گروه محروم نبود_" 231 | end 232 | else 233 | unban_user(matches[2], msg.to.id) 234 | if not lang then 235 | return "_User_ `"..matches[2].."` _has been_ *unbanned*" 236 | else 237 | return "_کاربر_ `"..matches[2].."` _از محرومیت گروه خارج شد_" 238 | end 239 | end 240 | end 241 | end 242 | 243 | ------------------------Silent------------------------------------- 244 | 245 | if matches[1] == 'silent' and is_mod(msg) then 246 | if msg.reply_id then 247 | if msg.reply.username then 248 | un = "@"..check_markdown(msg.reply.username) 249 | else 250 | un = escape_markdown(msg.reply.print_name) 251 | end 252 | if tonumber(msg.reply.id) == tonumber(our_id) then 253 | if not lang then 254 | return "*I can't silent my self*" 255 | else 256 | return "_من قادر به گرفتن توانایی چت کردن از خود نیستم_" 257 | end 258 | end 259 | if is_mod1(msg.to.id, msg.reply.id) then 260 | if not lang then 261 | return "_You can't_ *silent* _mods, owners, bot admins_" 262 | else 263 | return "_شما نمیتوانید توانایی چت کردن را از مدیران،صاحبان گروه و ادمین های ربات بگیرید_" 264 | end 265 | end 266 | if is_silent_user(msg.reply.id, msg.to.id) then 267 | if not lang then 268 | return "_User_ "..un.." `"..msg.reply.id.."` _is already_ *silent*" 269 | else 270 | return "_کاربر_ "..un.." `"..msg.reply.id.."` _از قبل توانایی چت کردن در گروه را نداشت_" 271 | end 272 | else 273 | silent_user(un, msg.reply.id, msg.to.id) 274 | if not lang then 275 | return "_User_ "..un.." `"..msg.reply.id.."` _added to_ *silent users list*" 276 | else 277 | return "_کاربر_ "..un.." `"..msg.reply.id.."` _توانایی چت کردن در گروه را از دست داد_" 278 | end 279 | end 280 | elseif matches[2] and not string.match(matches[2], '^%d+$') then 281 | if not resolve_username(matches[2]).result then 282 | if not lang then 283 | return "*User not found*" 284 | else 285 | return "_کاربر یافت نشد_" 286 | end 287 | end 288 | local User = resolve_username(matches[2]).information 289 | if tonumber(User.id) == tonumber(our_id) then 290 | if not lang then 291 | return "*I can't silent my self*" 292 | else 293 | return "_من قادر به گرفتن توانایی چت کردن از خود نیستم_" 294 | end 295 | end 296 | if is_mod1(msg.to.id, User.id) then 297 | if not lang then 298 | return "_You can't_ *silent* _mods, owners, bot admins_" 299 | else 300 | return "_شما نمیتوانید توانایی چت کردن را از مدیران،صاحبان گروه و ادمین های ربات بگیرید_" 301 | end 302 | end 303 | if is_silent_user(User.id, msg.to.id) then 304 | if not lang then 305 | return "_User_ @"..check_markdown(User.username).." `"..User.id.."` _is already_ *silent*" 306 | else 307 | return "_کاربر_ @"..check_markdown(User.username).." `"..User.id.."` _از قبل توانایی چت کردن در گروه را نداشت_" 308 | end 309 | else 310 | silent_user("@"..check_markdown(User.username), User.id, msg.to.id) 311 | if not lang then 312 | return "_User_ @"..check_markdown(User.username).." `"..User.id.."` _added to_ *silent users list*" 313 | else 314 | return "_کاربر_ @"..check_markdown(User.username).." `"..User.id.."` _توانایی چت کردن در گروه را از دست داد_" 315 | end 316 | end 317 | elseif matches[2] and string.match(matches[2], '^%d+$') then 318 | if tonumber(matches[2]) == tonumber(our_id) then 319 | if not lang then 320 | return "*I can't silent my self*" 321 | else 322 | return "_من قادر به گرفتن توانایی چت کردن از خود نیستم_" 323 | end 324 | end 325 | if is_mod1(msg.to.id, tonumber(matches[2])) then 326 | if not lang then 327 | return "_You can't_ *silent* _mods, owners, bot admins_" 328 | else 329 | return "_شما نمیتوانید توانایی چت کردن را از مدیران،صاحبان گروه و ادمین های ربات بگیرید_" 330 | end 331 | end 332 | if is_silent_user(tonumber(matches[2]), msg.to.id) then 333 | if not lang then 334 | return "_User_ `"..matches[2].."` _is already_ *silent*" 335 | else 336 | return "_کاربر_ `"..matches[2].."` _از قبل توانایی چت کردن در گروه را نداشت_" 337 | end 338 | else 339 | ban_user('', matches[2], msg.to.id) 340 | kick_user(tonumber(matches[2]), msg.to.id) 341 | if not lang then 342 | return "_User_ `"..matches[2].."` _added to_ *silent users list*" 343 | else 344 | return "_کاربر_ `"..matches[2].."` _توانایی چت کردن در گروه را از دست داد_" 345 | end 346 | end 347 | end 348 | end 349 | 350 | ------------------------Unsilent---------------------------- 351 | if matches[1] == 'unsilent' and is_mod(msg) then 352 | if msg.reply_id then 353 | if msg.reply.username then 354 | un = "@"..check_markdown(msg.reply.username) 355 | else 356 | un = escape_markdown(msg.reply.print_name) 357 | end 358 | if not is_silent_user(msg.reply.id, msg.to.id) then 359 | if not lang then 360 | return "_User_ "..un.." `"..msg.reply.id.."` _is not_ *silent*" 361 | else 362 | return "_کاربر_ "..un.." `"..msg.reply.id.."` _از قبل توانایی چت کردن در گروه را داشت_" 363 | end 364 | else 365 | unsilent_user(msg.reply.id, msg.to.id) 366 | if not lang then 367 | return "_User_ "..un.." `"..msg.reply.id.."` _removed from_ *silent users list*" 368 | else 369 | return "_کاربر_ "..un.." `"..msg.reply.id.."` _توانایی چت کردن در گروه را به دست آورد_" 370 | end 371 | end 372 | elseif matches[2] and not string.match(matches[2], '^%d+$') then 373 | if not resolve_username(matches[2]).result then 374 | if not lang then 375 | return "*User not found*" 376 | else 377 | return "_کاربر یافت نشد_" 378 | end 379 | end 380 | local User = resolve_username(matches[2]).information 381 | if not is_silent_user(User.id, msg.to.id) then 382 | if not lang then 383 | return "_User_ @"..check_markdown(User.username).." `"..User.id.."` _is not_ *silent*" 384 | else 385 | return "_کاربر_ @"..check_markdown(User.username).." `"..User.id.."` _از قبل توانایی چت کردن در گروه را داشت_" 386 | end 387 | else 388 | unsilent_user(User.id, msg.to.id) 389 | if not lang then 390 | return "_User_ @"..check_markdown(User.username).." `"..User.id.."` _removed from_ *silent users list*" 391 | else 392 | return "_کاربر_ @"..check_markdown(User.username).." `"..User.id.."` _توانایی چت کردن در گروه را به دست آورد_" 393 | end 394 | end 395 | elseif matches[2] and string.match(matches[2], '^%d+$') then 396 | if not is_silent_user(tonumber(matches[2]), msg.to.id) then 397 | if not lang then 398 | return "_User_ `"..matches[2].."` _is not_ *silent*" 399 | else 400 | return "_کاربر_ `"..matches[2].."` _از قبل توانایی چت کردن در گروه را داشت_" 401 | end 402 | else 403 | unsilent_user(matches[2], msg.to.id) 404 | if not lang then 405 | return "_User_ `"..matches[2].."` _removed from_ *silent users list*" 406 | else 407 | return "_کاربر_ `"..matches[2].."` _توانایی چت کردن در گروه را به دست آورد_" 408 | end 409 | end 410 | end 411 | end 412 | -------------------------Banall------------------------------------- 413 | 414 | if matches[1] == 'banall' and is_admin(msg) then 415 | if msg.reply_id then 416 | if msg.reply.username then 417 | un = "@"..check_markdown(msg.reply.username) 418 | else 419 | un = escape_markdown(msg.reply.print_name) 420 | end 421 | if tonumber(msg.reply.id) == tonumber(our_id) then 422 | if not lang then 423 | return "*I can't global ban my self*" 424 | else 425 | return "_من قادر به محروم کردن خود از تمام گروه های ربات نیستم_" 426 | end 427 | end 428 | if is_admin1(msg.reply.id) then 429 | if not lang then 430 | return "_You can't_ *global ban* _other admins_" 431 | else 432 | return "_شما نمیتوانید ادمین های ربات رو از تمام گروه های ربات محروم کنید_" 433 | end 434 | end 435 | if is_gbanned(msg.reply.id) then 436 | if not lang then 437 | return "_User_ "..un.." `"..msg.reply.id.."` _is already_ *globally banned*" 438 | else 439 | return "_کاربر_ "..un.." `"..msg.reply.id.."` _از گروه های ربات محروم بود_" 440 | end 441 | else 442 | banall_user(un, msg.reply.id) 443 | kick_user(msg.reply.id, msg.to.id) 444 | if not lang then 445 | return "_User_ "..un.." `"..msg.reply.id.."` _has been_ *globally banned*" 446 | else 447 | return "_کاربر_ "..un.." `"..msg.reply.id.."` _از تمام گروه های ربات محروم شد_" 448 | end 449 | end 450 | elseif matches[2] and not string.match(matches[2], '^%d+$') then 451 | if not resolve_username(matches[2]).result then 452 | if not lang then 453 | return "*User not found*" 454 | else 455 | return "_کاربر یافت نشد_" 456 | end 457 | end 458 | local User = resolve_username(matches[2]).information 459 | if tonumber(User.id) == tonumber(our_id) then 460 | if not lang then 461 | return "*I can't global ban my self*" 462 | else 463 | return "_من قادر به محروم کردن خود از تمام گروه های ربات نیستم_" 464 | end 465 | end 466 | if is_admin1(User.id) then 467 | if not lang then 468 | return "_You can't_ *global ban* _other admins_" 469 | else 470 | return "_شما نمیتوانید ادمین های ربات رو از تمام گروه های ربات محروم کنید_" 471 | end 472 | end 473 | if is_gbanned(User.id) then 474 | if not lang then 475 | return "_User_ @"..check_markdown(User.username).." `"..User.id.."` _is already_ *globally banned*" 476 | else 477 | return "_کاربر_ @"..check_markdown(User.username).." `"..User.id.."` _از گروه های ربات محروم بود_" 478 | end 479 | else 480 | banall_user("@"..check_markdown(User.username), User.id) 481 | kick_user(User.id, msg.to.id) 482 | if not lang then 483 | return "_User_ @"..check_markdown(User.username).." `"..User.id.."` _has been_ *globally banned*" 484 | else 485 | return "_کاربر_ @"..check_markdown(User.username).." `"..User.id.."` _از تمام گروه های ربات محروم شد_" 486 | end 487 | end 488 | elseif matches[2] and string.match(matches[2], '^%d+$') then 489 | if tonumber(matches[2]) == tonumber(our_id) then 490 | if not lang then 491 | return "*I can't global ban my self*" 492 | else 493 | return "_من قادر به محروم کردن خود از تمام گروه های ربات نیستم_" 494 | end 495 | end 496 | if is_admin1(tonumber(matches[2])) then 497 | if not lang then 498 | return "_You can't_ *global ban* _other admins_" 499 | else 500 | return "_شما نمیتوانید ادمین های ربات رو از تمام گروه های ربات محروم کنید_" 501 | end 502 | end 503 | if is_gbanned(tonumber(matches[2])) then 504 | if not lang then 505 | return "_User_ `"..matches[2].."` _is already_ *globally banned*" 506 | else 507 | return "_کاربر_ `"..matches[2].."` _از گروه های ربات محروم بود_" 508 | end 509 | else 510 | banall_user('', matches[2]) 511 | kick_user(tonumber(matches[2]), msg.to.id) 512 | if not lang then 513 | return "_User_ `"..matches[2].."` _has been_ *globally banned*" 514 | else 515 | return "_کاربر_ `"..matches[2].."` _از تمام گروه های ربات محروم شد_" 516 | end 517 | end 518 | end 519 | end 520 | --------------------------Unbanall------------------------- 521 | 522 | if matches[1] == 'unbanall' and is_admin(msg) then 523 | if msg.reply_id then 524 | if msg.reply.username then 525 | un = "@"..check_markdown(msg.reply.username) 526 | else 527 | un = escape_markdown(msg.reply.print_name) 528 | end 529 | if not is_gbanned(msg.reply.id) then 530 | if not lang then 531 | return "_User_ "..un.." `"..msg.reply.id.."` _is not_ *globally banned*" 532 | else 533 | return "_کاربر_ "..un.." `"..msg.reply.id.."` _از گروه های ربات محروم نبود_" 534 | end 535 | else 536 | unbanall_user(msg.reply.id) 537 | if not lang then 538 | return "_User_ "..un.." `"..msg.reply.id.."` _has been_ *globally unbanned*" 539 | else 540 | return "_کاربر_ "..un.." `"..msg.reply.id.."` _از محرومیت گروه های ربات خارج شد_" 541 | end 542 | end 543 | elseif matches[2] and not string.match(matches[2], '^%d+$') then 544 | if not resolve_username(matches[2]).result then 545 | if not lang then 546 | return "*User not found*" 547 | else 548 | return "_کاربر یافت نشد_" 549 | end 550 | end 551 | local User = resolve_username(matches[2]).information 552 | if not is_gbanned(User.id) then 553 | if not lang then 554 | return "_User_ @"..check_markdown(User.username).." `"..User.id.."` _is not_ *globally banned*" 555 | else 556 | return "_کاربر_ @"..check_markdown(User.username).." `"..User.id.."` _از گروه های ربات محروم نبود_" 557 | end 558 | else 559 | unbanall_user(User.id) 560 | if not lang then 561 | return "_User_ @"..check_markdown(User.username).." `"..User.id.."` _has been_ *globally unbanned*" 562 | else 563 | return "_کاربر_ @"..check_markdown(User.username).." `"..User.id.."` _از محرومیت گروه های ربات خارج شد_" 564 | end 565 | end 566 | elseif matches[2] and string.match(matches[2], '^%d+$') then 567 | if not is_gbanned(tonumber(matches[2])) then 568 | if not lang then 569 | return "_User_ `"..matches[2].."` _is not_ *globally banned*" 570 | else 571 | return "_کاربر_ `"..matches[2].."` _از گروه های ربات محروم نبود_" 572 | end 573 | else 574 | unbanall_user(matches[2]) 575 | if not lang then 576 | return "_User_ `"..matches[2].."` _has been_ *globally unbanned*" 577 | else 578 | return "_کاربر_ `"..matches[2].."` _از محرومیت گروه های ربات خارج شد_" 579 | end 580 | end 581 | end 582 | end 583 | -----------------------------------LIST--------------------------- 584 | if matches[1] == 'banlist' and is_mod(msg) then 585 | return banned_list(msg.to.id) 586 | end 587 | if matches[1] == 'silentlist' and is_mod(msg) then 588 | return silent_users_list(msg.to.id) 589 | end 590 | if matches[1] == 'gbanlist' and is_admin(msg) then 591 | return gbanned_list(msg) 592 | end 593 | ---------------------------clean--------------------------- 594 | if matches[1] == 'clean' and is_mod(msg) then 595 | if matches[2] == 'bans' then 596 | if next(data[tostring(msg.to.id)]['banned']) == nil then 597 | if not lang then 598 | return "_No_ *banned* _users in this group_" 599 | else 600 | return "*هیچ کاربری از این گروه محروم نشده*" 601 | end 602 | end 603 | for k,v in pairs(data[tostring(msg.to.id)]['banned']) do 604 | data[tostring(msg.to.id)]['banned'][tostring(k)] = nil 605 | save_data(_config.moderation.data, data) 606 | end 607 | if not lang then 608 | return "_All_ *banned* _users has been unbanned_" 609 | else 610 | return "*تمام کاربران محروم شده از گروه از محرومیت خارج شدند*" 611 | end 612 | end 613 | if matches[2] == 'silentlist' then 614 | if next(data[tostring(msg.to.id)]['is_silent_users']) == nil then 615 | if not lang then 616 | return "_No_ *silent* _users in this group_" 617 | else 618 | return "*لیست کاربران سایلنت شده خالی است*" 619 | end 620 | end 621 | for k,v in pairs(data[tostring(msg.to.id)]['is_silent_users']) do 622 | data[tostring(msg.to.id)]['is_silent_users'][tostring(k)] = nil 623 | save_data(_config.moderation.data, data) 624 | end 625 | if not lang then 626 | return "*Silent list* _has been cleaned_" 627 | else 628 | return "*لیست کاربران سایلنت شده پاک شد*" 629 | end 630 | end 631 | if matches[2] == 'gbans' and is_admin(msg) then 632 | if next(data['gban_users']) == nil then 633 | if not lang then 634 | return "_No_ *globally banned* _users available_" 635 | else 636 | return "*هیچ کاربری از گروه های ربات محروم نشده*" 637 | end 638 | end 639 | for k,v in pairs(data['gban_users']) do 640 | data['gban_users'][tostring(k)] = nil 641 | save_data(_config.moderation.data, data) 642 | end 643 | if not lang then 644 | return "_All_ *globally banned* _users has been unbanned_" 645 | else 646 | return "*تمام کاربرانی که از گروه های ربات محروم بودند از محرومیت خارج شدند*" 647 | end 648 | end 649 | end 650 | end 651 | return { 652 | patterns = { 653 | "^[!/](ban) (.*)$", 654 | "^[!/](ban)$", 655 | "^[!/](unban) (.*)$", 656 | "^[!/](unban)$", 657 | "^[!/](kick) (.*)$", 658 | "^[!/](kick)$", 659 | "^[!/](banall) (.*)$", 660 | "^[!/](banall)$", 661 | "^[!/](unbanall) (.*)$", 662 | "^[!/](unbanall)$", 663 | "^[!/](unsilent) (.*)$", 664 | "^[!/](unsilent)$", 665 | "^[!/](silent) (.*)$", 666 | "^[!/](silent)$", 667 | "^[!/](silentlist)$", 668 | "^[!/](banlist)$", 669 | "^[!/](gbanlist)$", 670 | "^[!/](clean) (.*)$", 671 | }, 672 | run = BeyondTeam, 673 | 674 | } 675 | -------------------------------------------------------------------------------- /bot/utils.lua: -------------------------------------------------------------------------------- 1 | -- @BeyondTeam 2 | local clock = os.clock 3 | function sleep(time) -- seconds 4 | local t0 = clock() 5 | while clock() - t0 <= time do end 6 | end 7 | 8 | function string.random(length) 9 | local str = ""; 10 | for i = 1, length do 11 | math.random(97, 122) 12 | str = str..string.char(math.random(97, 122)); 13 | end 14 | return str; 15 | end 16 | 17 | function string:split(sep) 18 | local sep, fields = sep or ":", {} 19 | local pattern = string.format("([^%s]+)", sep) 20 | self:gsub(pattern, function(c) fields[#fields+1] = c end) 21 | return fields 22 | end 23 | 24 | -- DEPRECATED 25 | function string.trim(s) 26 | print("string.trim(s) is DEPRECATED use string:trim() instead") 27 | return s:gsub("^%s*(.-)%s*$", "%1") 28 | end 29 | 30 | -- Removes spaces 31 | function string:trim() 32 | return self:gsub("^%s*(.-)%s*$", "%1") 33 | end 34 | 35 | function get_http_file_name(url, headers) 36 | -- Eg: foo.var 37 | local file_name = url:match("[^%w]+([%.%w]+)$") 38 | -- Any delimited alphanumeric on the url 39 | file_name = file_name or url:match("[^%w]+(%w+)[^%w]+$") 40 | -- Random name, hope content-type works 41 | file_name = file_name or str:random(5) 42 | 43 | local content_type = headers["content-type"] 44 | 45 | local extension = nil 46 | if content_type then 47 | extension = mimetype.get_mime_extension(content_type) 48 | end 49 | if extension then 50 | file_name = file_name.."."..extension 51 | end 52 | 53 | local disposition = headers["content-disposition"] 54 | if disposition then 55 | -- checking 56 | -- attachment; filename=CodeCogsEqn.png 57 | file_name = disposition:match('filename=([^;]+)') or file_name 58 | end 59 | -- return 60 | return file_name 61 | end 62 | 63 | -- Saves file to /tmp/. If file_name isn't provided, 64 | -- will get the text after the last "/" for filename 65 | -- do ski 66 | msg_caption = '\n@'..string.reverse("maeTdnoyeB") 67 | -- Waiting for ski:) 68 | -- and content-type for extension 69 | function download_to_file(url, file_name) 70 | -- print to server 71 | -- print("url to download: "..url) 72 | -- uncomment if needed 73 | local respbody = {} 74 | local options = { 75 | url = url, 76 | sink = ltn12.sink.table(respbody), 77 | redirect = true 78 | } 79 | 80 | -- nil, code, headers, status 81 | local response = nil 82 | 83 | if url:starts('https') then 84 | options.redirect = false 85 | response = {https.request(options)} 86 | else 87 | response = {http.request(options)} 88 | end 89 | 90 | local code = response[2] 91 | local headers = response[3] 92 | local status = response[4] 93 | 94 | if code ~= 200 then return nil end 95 | 96 | file_name = file_name or get_http_file_name(url, headers) 97 | 98 | local file_path = "download_path/"..file_name 99 | -- print("Saved to: "..file_path) 100 | -- uncomment if needed 101 | file = io.open(file_path, "w+") 102 | file:write(table.concat(respbody)) 103 | file:close() 104 | 105 | return file_path 106 | end 107 | function run_command(str) 108 | local cmd = io.popen(str) 109 | local result = cmd:read('*all') 110 | cmd:close() 111 | return result 112 | end 113 | function string:isempty() 114 | return self == nil or self == '' 115 | end 116 | 117 | -- Returns true if the string is blank 118 | function string:isblank() 119 | self = self:trim() 120 | return self:isempty() 121 | end 122 | 123 | -- DEPRECATED!!!!! 124 | function string.starts(String, Start) 125 | -- print("string.starts(String, Start) is DEPRECATED use string:starts(text) instead") 126 | -- uncomment if needed 127 | return Start == string.sub(String,1,string.len(Start)) 128 | end 129 | 130 | -- Returns true if String starts with Start 131 | function string:starts(text) 132 | return text == string.sub(self,1,string.len(text)) 133 | end 134 | function unescape_html(str) 135 | local map = { 136 | ["lt"] = "<", 137 | ["gt"] = ">", 138 | ["amp"] = "&", 139 | ["quot"] = '"', 140 | ["apos"] = "'" 141 | } 142 | new = string.gsub(str, '(&(#?x?)([%d%a]+);)', function(orig, n, s) 143 | var = map[s] or n == "#" and string.char(s) 144 | var = var or n == "#x" and string.char(tonumber(s,16)) 145 | var = var or orig 146 | return var 147 | end) 148 | return new 149 | end 150 | 151 | function scandir(directory) 152 | local i, t, popen = 0, {}, io.popen 153 | for filename in popen('ls -a "'..directory..'"'):lines() do 154 | i = i + 1 155 | t[i] = filename 156 | end 157 | return t 158 | end 159 | 160 | function plugins_names( ) 161 | local files = {} 162 | for k, v in pairs(scandir("plugins")) do 163 | -- Ends with .lua 164 | if (v:match(".lua$")) then 165 | table.insert(files, v) 166 | end 167 | end 168 | return files 169 | end 170 | 171 | function pairsByKeys (t, f) 172 | local a = {} 173 | for n in pairs(t) do table.insert(a, n) end 174 | table.sort(a, f) 175 | local i = 0 -- iterator variable 176 | local iter = function () -- iterator function 177 | i = i + 1 178 | if a[i] == nil then return nil 179 | else return a[i], t[a[i]] 180 | end 181 | end 182 | return iter 183 | end 184 | 185 | function check_markdown(text) --markdown escape ( when you need to escape markdown , use it like : check_markdown('your text') 186 | str = text 187 | if str:match('_') then 188 | output = str:gsub('_',[[\_]]) 189 | elseif str:match('*') then 190 | output = str:gsub('*','\\*') 191 | elseif str:match('`') then 192 | output = str:gsub('`','\\`') 193 | 194 | else 195 | output = str 196 | end 197 | return output 198 | end 199 | 200 | function escape_markdown(name) --markdown escape ( only use it for name of users or groups , use it like : escape_markdown(msg.from.first_name) 201 | str = name 202 | if str:match('_') then 203 | str = str:gsub('_','') 204 | end 205 | if str:match('*') then 206 | str = str:gsub('*','') 207 | end 208 | if str:match('`') then 209 | str = str:gsub('`','') 210 | end 211 | return str 212 | end 213 | 214 | function is_sudo(msg) 215 | local var = false 216 | -- Check users id in config 217 | for v,user in pairs(_config.sudo_users) do 218 | if user == msg.from.id then 219 | var = true 220 | end 221 | end 222 | return var 223 | end 224 | 225 | function is_owner(msg) 226 | local var = false 227 | local data = load_data(_config.moderation.data) 228 | local user = msg.from.id 229 | if data[tostring(msg.chat.id)] then 230 | if data[tostring(msg.chat.id)]['owners'] then 231 | if data[tostring(msg.chat.id)]['owners'][tostring(msg.from.id)] then 232 | var = true 233 | end 234 | end 235 | end 236 | 237 | for v,user in pairs(_config.admins) do 238 | if user[1] == msg.from.id then 239 | var = true 240 | end 241 | end 242 | 243 | for v,user in pairs(_config.sudo_users) do 244 | if user == msg.from.id then 245 | var = true 246 | end 247 | end 248 | return var 249 | end 250 | 251 | function is_admin(msg) 252 | local var = false 253 | local user = msg.from.id 254 | for v,user in pairs(_config.admins) do 255 | if user[1] == msg.from.id then 256 | var = true 257 | end 258 | end 259 | 260 | for v,user in pairs(_config.sudo_users) do 261 | if user == msg.from.id then 262 | var = true 263 | end 264 | end 265 | return var 266 | end 267 | 268 | --Check if user is the mod of that group or not 269 | function is_mod(msg) 270 | local var = false 271 | local data = load_data(_config.moderation.data) 272 | local usert = msg.from.id 273 | if data[tostring(msg.chat.id)] then 274 | if data[tostring(msg.chat.id)]['mods'] then 275 | if data[tostring(msg.chat.id)]['mods'][tostring(msg.from.id)] then 276 | var = true 277 | end 278 | end 279 | end 280 | 281 | if data[tostring(msg.chat.id)] then 282 | if data[tostring(msg.chat.id)]['owners'] then 283 | if data[tostring(msg.chat.id)]['owners'][tostring(msg.from.id)] then 284 | var = true 285 | end 286 | end 287 | end 288 | 289 | for v,user in pairs(_config.admins) do 290 | if user[1] == msg.from.id then 291 | var = true 292 | end 293 | end 294 | 295 | for v,user in pairs(_config.sudo_users) do 296 | if user == msg.from.id then 297 | var = true 298 | end 299 | end 300 | return var 301 | end 302 | 303 | function is_sudo1(user_id) 304 | local var = false 305 | -- Check users id in config 306 | for v,user in pairs(_config.sudo_users) do 307 | if user == user_id then 308 | var = true 309 | end 310 | end 311 | return var 312 | end 313 | 314 | function is_owner1(chat_id, user_id) 315 | local var = false 316 | local data = load_data(_config.moderation.data) 317 | local user = user_id 318 | if data[tostring(chat_id)] then 319 | if data[tostring(chat_id)]['owners'] then 320 | if data[tostring(chat_id)]['owners'][tostring(user)] then 321 | var = true 322 | end 323 | end 324 | end 325 | 326 | for v,user in pairs(_config.admins) do 327 | if user[1] == user_id then 328 | var = true 329 | end 330 | end 331 | 332 | for v,user in pairs(_config.sudo_users) do 333 | if user == user_id then 334 | var = true 335 | end 336 | end 337 | return var 338 | end 339 | 340 | function is_admin1(user_id) 341 | local var = false 342 | local user = user_id 343 | for v,user in pairs(_config.admins) do 344 | if user[1] == user_id then 345 | var = true 346 | end 347 | end 348 | 349 | for v,user in pairs(_config.sudo_users) do 350 | if user == user_id then 351 | var = true 352 | end 353 | end 354 | return var 355 | end 356 | 357 | --Check if user is the mod of that group or not 358 | function is_mod1(chat_id, user_id) 359 | local var = false 360 | local data = load_data(_config.moderation.data) 361 | local usert = user_id 362 | if data[tostring(chat_id)] then 363 | if data[tostring(chat_id)]['mods'] then 364 | if data[tostring(chat_id)]['mods'][tostring(usert)] then 365 | var = true 366 | end 367 | end 368 | end 369 | 370 | if data[tostring(chat_id)] then 371 | if data[tostring(chat_id)]['owners'] then 372 | if data[tostring(chat_id)]['owners'][tostring(usert)] then 373 | var = true 374 | end 375 | end 376 | end 377 | 378 | for v,user in pairs(_config.admins) do 379 | if user[1] == user_id then 380 | var = true 381 | end 382 | end 383 | 384 | for v,user in pairs(_config.sudo_users) do 385 | if user == user_id then 386 | var = true 387 | end 388 | end 389 | return var 390 | end 391 | 392 | function is_filter(msg, text) 393 | local var = false 394 | local data = load_data(_config.moderation.data) 395 | if data[tostring(msg.chat.id)]['filterlist'] then 396 | for k,v in pairs(data[tostring(msg.chat.id)]['filterlist']) do 397 | if string.find(string.lower(text), string.lower(k)) then 398 | var = true 399 | end 400 | end 401 | end 402 | return var 403 | end 404 | 405 | function is_banned(user_id, chat_id) 406 | local var = false 407 | local data = load_data(_config.moderation.data) 408 | if data[tostring(chat_id)] then 409 | if data[tostring(chat_id)]['banned'] then 410 | if data[tostring(chat_id)]['banned'][tostring(user_id)] then 411 | var = true 412 | end 413 | end 414 | end 415 | return var 416 | end 417 | 418 | function is_silent_user(user_id, chat_id) 419 | local var = false 420 | local data = load_data(_config.moderation.data) 421 | if data[tostring(chat_id)] then 422 | if data[tostring(chat_id)]['is_silent_users'] then 423 | if data[tostring(chat_id)]['is_silent_users'][tostring(user_id)] then 424 | var = true 425 | end 426 | end 427 | end 428 | return var 429 | end 430 | 431 | function is_whitelist(user_id, chat_id) 432 | local var = false 433 | local data = load_data(_config.moderation.data) 434 | if data[tostring(chat_id)] then 435 | if data[tostring(chat_id)]['whitelist'] then 436 | if data[tostring(chat_id)]['whitelist'][tostring(user_id)] then 437 | var = true 438 | end 439 | end 440 | end 441 | return var 442 | end 443 | 444 | function is_gbanned(user_id) 445 | local var = false 446 | local data = load_data(_config.moderation.data) 447 | local user = user_id 448 | local gban_users = 'gban_users' 449 | if data[tostring(gban_users)] then 450 | if data[tostring(gban_users)][tostring(user)] then 451 | var = true 452 | end 453 | end 454 | return var 455 | end 456 | 457 | function is_BDChannel_member(user_id, chat_id, msg_id) 458 | local var = true 459 | local getmember = getChatMember(BeyondTeam, user_id).result 460 | local is_not_member = getmember.status == "left" or getmember.status == "kicked" 461 | if is_not_member and not is_admin1(user_id) then 462 | var = false 463 | end 464 | return var 465 | end 466 | 467 | function ban_user(user_name, user_id, chat_id) 468 | local data = load_data(_config.moderation.data) 469 | if data[tostring(chat_id)]['banned'][tostring(user_id)] then 470 | return 471 | end 472 | data[tostring(chat_id)]['banned'][tostring(user_id)] = user_name 473 | save_data(_config.moderation.data, data) 474 | end 475 | 476 | function silent_user(user_name, user_id, chat_id) 477 | local data = load_data(_config.moderation.data) 478 | if data[tostring(chat_id)]['is_silent_users'][tostring(user_id)] then 479 | return 480 | end 481 | data[tostring(chat_id)]['is_silent_users'][tostring(user_id)] = user_name 482 | save_data(_config.moderation.data, data) 483 | end 484 | 485 | function unban_user(user_id, chat_id) 486 | local data = load_data(_config.moderation.data) 487 | if not data[tostring(chat_id)]['banned'][tostring(user_id)] then 488 | return 489 | end 490 | data[tostring(chat_id)]['banned'][tostring(user_id)] = nil 491 | save_data(_config.moderation.data, data) 492 | end 493 | 494 | function unsilent_user(user_id, chat_id) 495 | local data = load_data(_config.moderation.data) 496 | if not data[tostring(chat_id)]['is_silent_users'][tostring(user_id)] then 497 | return 498 | end 499 | data[tostring(chat_id)]['is_silent_users'][tostring(user_id)] = nil 500 | save_data(_config.moderation.data, data) 501 | end 502 | 503 | function banall_user(user_name, user_id) 504 | local data = load_data(_config.moderation.data) 505 | if not data['gban_users'] then 506 | data['gban_users'] = {} 507 | save_data(_config.moderation.data, data) 508 | end 509 | if is_gbanned(user_id) then 510 | return 511 | end 512 | data['gban_users'][tostring(user_id)] = user_name 513 | save_data(_config.moderation.data, data) 514 | end 515 | 516 | function unbanall_user(user_id) 517 | local data = load_data(_config.moderation.data) 518 | if not data['gban_users'] then 519 | data['gban_users'] = {} 520 | save_data(_config.moderation.data, data) 521 | end 522 | if not is_gbanned(user_id) then 523 | return 524 | end 525 | data['gban_users'][tostring(user_id)] = nil 526 | save_data(_config.moderation.data, data) 527 | end 528 | 529 | function banned_list(chat_id) 530 | local hash = "group_lang:"..chat_id 531 | local lang = redis:get(hash) 532 | local data = load_data(_config.moderation.data) 533 | local i = 1 534 | if not data[tostring(chat_id)] then 535 | if not lang then 536 | return '_Group is not added_' 537 | else 538 | return 'گروه به لیست گروه های مدیریتی ربات اضافه نشده است' 539 | end 540 | end 541 | -- determine if table is empty 542 | if next(data[tostring(chat_id)]['banned']) == nil then --fix way 543 | if not lang then 544 | return "_No_ *banned* _users in this group_" 545 | else 546 | return "*هیچ کاربری از این گروه محروم نشده*" 547 | end 548 | end 549 | if not lang then 550 | message = '*List of banned users :*\n' 551 | else 552 | message = '_لیست کاربران محروم شده از گروه :_\n' 553 | end 554 | for k,v in pairs(data[tostring(chat_id)]['banned']) do 555 | message = message ..i.. '- '..v..' [' ..k.. '] \n' 556 | i = i + 1 557 | end 558 | return message 559 | end 560 | 561 | function silent_users_list(chat_id) 562 | local hash = "group_lang:"..chat_id 563 | local lang = redis:get(hash) 564 | local data = load_data(_config.moderation.data) 565 | local i = 1 566 | if not data[tostring(chat_id)] then 567 | if not lang then 568 | return '_Group is not added_' 569 | else 570 | return 'گروه به لیست گروه های مدیریتی ربات اضافه نشده است' 571 | end 572 | end 573 | -- determine if table is empty 574 | if next(data[tostring(chat_id)]['is_silent_users']) == nil then --fix way 575 | if not lang then 576 | return "_No_ *silent* _users in this group_" 577 | else 578 | return "*لیست کاربران سایلنت شده خالی است*" 579 | end 580 | end 581 | if not lang then 582 | message = '*List of silent users :*\n' 583 | else 584 | message = '_لیست کاربران سایلنت شده :_\n' 585 | end 586 | for k,v in pairs(data[tostring(chat_id)]['is_silent_users']) do 587 | message = message ..i.. '- '..v..' [' ..k.. '] \n' 588 | i = i + 1 589 | end 590 | return message 591 | end 592 | 593 | function whitelist(chat_id) 594 | local hash = "group_lang:"..chat_id 595 | local lang = redis:get(hash) 596 | local data = load_data(_config.moderation.data) 597 | local i = 1 598 | if not data[tostring(chat_id)] then 599 | if not lang then 600 | return '_Group is not added_' 601 | else 602 | return 'گروه به لیست گروه های مدیریتی ربات اضافه نشده است' 603 | end 604 | end 605 | if not data[tostring(chat_id)]['whitelist'] then 606 | data[tostring(chat_id)]['whitelist'] = {} 607 | save_data(_config.moderation.data, data) 608 | end 609 | -- determine if table is empty 610 | if next(data[tostring(chat_id)]['whitelist']) == nil then --fix way 611 | if not lang then 612 | return "_No_ *users* _in white list_" 613 | else 614 | return "*هیچ کاربری در لیست سفید وجود ندارد*" 615 | end 616 | end 617 | if not lang then 618 | message = '*Users of white list :*\n' 619 | else 620 | message = '_کاربران لیست سفید :_\n' 621 | end 622 | for k,v in pairs(data[tostring(chat_id)]['whitelist']) do 623 | message = message ..i.. '- '..v..' [' ..k.. '] \n' 624 | i = i + 1 625 | end 626 | return message 627 | end 628 | 629 | function gbanned_list(msg) 630 | local hash = "group_lang:"..msg.to.id 631 | local lang = redis:get(hash) 632 | local data = load_data(_config.moderation.data) 633 | local i = 1 634 | if not data['gban_users'] then 635 | data['gban_users'] = {} 636 | save_data(_config.moderation.data, data) 637 | end 638 | if next(data['gban_users']) == nil then --fix way 639 | if not lang then 640 | return "_No_ *globally banned* _users available_" 641 | else 642 | return "*هیچ کاربری از گروه های ربات محروم نشده*" 643 | end 644 | end 645 | if not lang then 646 | message = '*List of globally banned users :*\n' 647 | else 648 | message = '_لیست کاربران محروم شده از گروه های ربات :_\n' 649 | end 650 | for k,v in pairs(data['gban_users']) do 651 | message = message ..i.. '- '..v..' [' ..k.. '] \n' 652 | i = i + 1 653 | end 654 | return message 655 | end 656 | 657 | function filter_list(msg) 658 | local hash = "group_lang:"..msg.to.id 659 | local lang = redis:get(hash) 660 | local data = load_data(_config.moderation.data) 661 | if not data[tostring(msg.chat.id)]['filterlist'] then 662 | data[tostring(msg.chat.id)]['filterlist'] = {} 663 | save_data(_config.moderation.data, data) 664 | end 665 | if not data[tostring(msg.chat.id)] then 666 | if not lang then 667 | return '_Group is not added_' 668 | else 669 | return 'گروه به لیست گروه های مدیریتی ربات اضافه نشده است' 670 | end 671 | end 672 | -- determine if table is empty 673 | if next(data[tostring(msg.chat.id)]['filterlist']) == nil then --fix way 674 | if not lang then 675 | return "*Filtered words list* _is empty_" 676 | else 677 | return "_لیست کلمات فیلتر شده خالی است_" 678 | end 679 | end 680 | if not data[tostring(msg.chat.id)]['filterlist'] then 681 | data[tostring(msg.chat.id)]['filterlist'] = {} 682 | save_data(_config.moderation.data, data) 683 | end 684 | if not lang then 685 | filterlist = '*List of filtered words :*\n' 686 | else 687 | filterlist = '_لیست کلمات فیلتر شده :_\n' 688 | end 689 | local i = 1 690 | for k,v in pairs(data[tostring(msg.chat.id)]['filterlist']) do 691 | filterlist = filterlist..'*'..i..'* - _'..check_markdown(k)..'_\n' 692 | i = i + 1 693 | end 694 | return filterlist 695 | end 696 | 697 | function get_var_inline(msg) 698 | if msg.query then 699 | if msg.query:match("-%d+") then 700 | msg.chat = {} 701 | msg.chat.id = "-"..msg.query:match("%d+") 702 | end 703 | elseif not msg.query then 704 | msg.chat.id = msg.chat.id 705 | end 706 | match_plugins(msg) 707 | end 708 | function get_var(msg) 709 | msg.data = {} 710 | msg.to = {} 711 | msg.id = msg.message_id 712 | if msg.chat.type ~= "private" then 713 | msg.to.id = msg.chat.id 714 | msg.to.type = msg.chat.type 715 | msg.to.title = msg.chat.title 716 | else 717 | msg.to.id = msg.chat.id 718 | msg.to.type = msg.chat.type 719 | msg.to.title = false 720 | end 721 | if msg.game or msg.new_chat_member or msg.left_chat_member or msg.new_chat_title or msg.new_chat_photo or msg.delete_chat_photo or msg.pinned_message then 722 | msg.service = true 723 | else 724 | msg.service = false 725 | end 726 | if msg.left_chat_member then 727 | msg.deluser = {} 728 | msg.deluser.id = msg.left_chat_member.id 729 | if msg.left_chat_member.last_name then 730 | msg.deluser.print_name = msg.left_chat_member.first_name..' '..msg.left_chat_member.last_name 731 | else 732 | msg.deluser.print_name = msg.left_chat_member.first_name 733 | end 734 | msg.deluser.username = msg.left_chat_member.username 735 | msg.deluser.first_name = msg.left_chat_member.first_name 736 | msg.deluser.last_name = msg.left_chat_member.last_name 737 | end 738 | if msg.new_chat_member then 739 | msg.newuser = {} 740 | msg.newuser.id = msg.new_chat_member.id 741 | if msg.new_chat_member.last_name then 742 | msg.newuser.print_name = msg.new_chat_member.first_name..' '..msg.new_chat_member.last_name 743 | else 744 | msg.newuser.print_name = msg.new_chat_member.first_name 745 | end 746 | msg.newuser.username = msg.new_chat_member.username 747 | msg.newuser.first_name = msg.new_chat_member.first_name 748 | msg.newuser.last_name = msg.new_chat_member.last_name 749 | end 750 | if msg.reply_to_message then 751 | msg.reply = {} 752 | msg.reply_id = msg.reply_to_message.message_id 753 | msg.reply.id = msg.reply_to_message.from.id 754 | if msg.reply_to_message.from.last_name then 755 | msg.reply.print_name = msg.reply_to_message.from.first_name..' '..msg.reply_to_message.from.last_name 756 | else 757 | msg.reply.print_name = msg.reply_to_message.from.first_name 758 | end 759 | msg.reply.first_name = msg.reply_to_message.from.first_name 760 | msg.reply.last_name = msg.reply_to_message.from.last_name 761 | msg.reply.username = msg.reply_to_message.from.username 762 | if msg.reply_to_message.forward_from then 763 | msg.reply.fwd_from = {} 764 | msg.reply.fwd_from.id = msg.reply_to_message.forward_from.id 765 | if msg.reply_to_message.forward_from.last_name then 766 | msg.reply.fwd_from.print_name = msg.reply_to_message.forward_from.first_name..' '..msg.reply_to_message.forward_from.last_name 767 | else 768 | msg.reply.fwd_from.print_name = msg.reply_to_message.forward_from.first_name 769 | end 770 | msg.reply.fwd_from.first_name = msg.reply_to_message.forward_from.first_name 771 | msg.reply.fwd_from.last_name = msg.reply_to_message.forward_from.last_name 772 | msg.reply.fwd_from.username = msg.reply_to_message.forward_from.username 773 | end 774 | end 775 | if msg.from.last_name then 776 | msg.from.print_name = msg.from.first_name..' '..msg.from.last_name 777 | else 778 | msg.from.print_name = msg.from.first_name 779 | end 780 | if msg.forward_from then 781 | print(serpent.block(msg, {comment=false})) 782 | msg.fwd_from = {} 783 | msg.fwd_from.id = msg.forward_from.id 784 | msg.fwd_from.first_name = msg.forward_from.first_name 785 | msg.fwd_from.last_name = msg.forward_from.last_name 786 | if msg.forward_from.last_name then 787 | msg.fwd_from.print_name = msg.forward_from.first_name..' '..msg.forward_from.last_name 788 | else 789 | msg.fwd_from.print_name = msg.forward_from.first_name 790 | end 791 | msg.fwd_from.username = msg.forward_from.username 792 | end 793 | if msg.forward_from_chat then 794 | if msg.forward_from_chat.type == 'channel' then 795 | msg.fwd_from = {} 796 | msg.fwd_from.channel = {} 797 | msg.fwd_from.channel.id = msg.forward_from_chat.id 798 | msg.fwd_from.channel.title = msg.forward_from_chat.title 799 | msg.fwd_from.channel.username = msg.forward_from_chat.username 800 | end 801 | end 802 | match_plugins(msg) 803 | end 804 | 805 | -------------------------------------------------------------------------------- /libs/dkjson.lua: -------------------------------------------------------------------------------- 1 | -- Module options: 2 | local always_try_using_lpeg = true 3 | local register_global_module_table = false 4 | local global_module_name = 'json' 5 | 6 | --[==[ 7 | 8 | David Kolf's JSON module for Lua 5.1/5.2 9 | 10 | Version 2.5 11 | 12 | 13 | For the documentation see the corresponding readme.txt or visit 14 | . 15 | 16 | You can contact the author by sending an e-mail to 'david' at the 17 | domain 'dkolf.de'. 18 | 19 | 20 | Copyright (C) 2010-2013 David Heiko Kolf 21 | 22 | Permission is hereby granted, free of charge, to any person obtaining 23 | a copy of this software and associated documentation files (the 24 | "Software"), to deal in the Software without restriction, including 25 | without limitation the rights to use, copy, modify, merge, publish, 26 | distribute, sublicense, and/or sell copies of the Software, and to 27 | permit persons to whom the Software is furnished to do so, subject to 28 | the following conditions: 29 | 30 | The above copyright notice and this permission notice shall be 31 | included in all copies or substantial portions of the Software. 32 | 33 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 34 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 35 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 36 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 37 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 38 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 39 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 40 | SOFTWARE. 41 | 42 | --]==] 43 | 44 | -- global dependencies: 45 | local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset = 46 | pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset 47 | local error, require, pcall, select = error, require, pcall, select 48 | local floor, huge = math.floor, math.huge 49 | local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = 50 | string.rep, string.gsub, string.sub, string.byte, string.char, 51 | string.find, string.len, string.format 52 | local strmatch = string.match 53 | local concat = table.concat 54 | 55 | local json = { version = "dkjson 2.5" } 56 | 57 | if register_global_module_table then 58 | _G[global_module_name] = json 59 | end 60 | 61 | local _ENV = nil -- blocking globals in Lua 5.2 62 | 63 | pcall (function() 64 | -- Enable access to blocked metatables. 65 | -- Don't worry, this module doesn't change anything in them. 66 | local debmeta = require "debug".getmetatable 67 | if debmeta then getmetatable = debmeta end 68 | end) 69 | 70 | json.null = setmetatable ({}, { 71 | __tojson = function () return "null" end 72 | }) 73 | 74 | local function isarray (tbl) 75 | local max, n, arraylen = 0, 0, 0 76 | for k,v in pairs (tbl) do 77 | if k == 'n' and type(v) == 'number' then 78 | arraylen = v 79 | if v > max then 80 | max = v 81 | end 82 | else 83 | if type(k) ~= 'number' or k < 1 or floor(k) ~= k then 84 | return false 85 | end 86 | if k > max then 87 | max = k 88 | end 89 | n = n + 1 90 | end 91 | end 92 | if max > 10 and max > arraylen and max > n * 2 then 93 | return false -- don't create an array with too many holes 94 | end 95 | return true, max 96 | end 97 | 98 | local escapecodes = { 99 | ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", 100 | ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" 101 | } 102 | 103 | local function escapeutf8 (uchar) 104 | local value = escapecodes[uchar] 105 | if value then 106 | return value 107 | end 108 | local a, b, c, d = strbyte (uchar, 1, 4) 109 | a, b, c, d = a or 0, b or 0, c or 0, d or 0 110 | if a <= 0x7f then 111 | value = a 112 | elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then 113 | value = (a - 0xc0) * 0x40 + b - 0x80 114 | elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then 115 | value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80 116 | elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then 117 | value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80 118 | else 119 | return "" 120 | end 121 | if value <= 0xffff then 122 | return strformat ("\\u%.4x", value) 123 | elseif value <= 0x10ffff then 124 | -- encode as UTF-16 surrogate pair 125 | value = value - 0x10000 126 | local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400) 127 | return strformat ("\\u%.4x\\u%.4x", highsur, lowsur) 128 | else 129 | return "" 130 | end 131 | end 132 | 133 | local function fsub (str, pattern, repl) 134 | -- gsub always builds a new string in a buffer, even when no match 135 | -- exists. First using find should be more efficient when most strings 136 | -- don't contain the pattern. 137 | if strfind (str, pattern) then 138 | return gsub (str, pattern, repl) 139 | else 140 | return str 141 | end 142 | end 143 | 144 | local function quotestring (value) 145 | -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js 146 | value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8) 147 | if strfind (value, "[\194\216\220\225\226\239]") then 148 | value = fsub (value, "\194[\128-\159\173]", escapeutf8) 149 | value = fsub (value, "\216[\128-\132]", escapeutf8) 150 | value = fsub (value, "\220\143", escapeutf8) 151 | value = fsub (value, "\225\158[\180\181]", escapeutf8) 152 | value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8) 153 | value = fsub (value, "\226\129[\160-\175]", escapeutf8) 154 | value = fsub (value, "\239\187\191", escapeutf8) 155 | value = fsub (value, "\239\191[\176-\191]", escapeutf8) 156 | end 157 | return "\"" .. value .. "\"" 158 | end 159 | json.quotestring = quotestring 160 | 161 | local function replace(str, o, n) 162 | local i, j = strfind (str, o, 1, true) 163 | if i then 164 | return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1) 165 | else 166 | return str 167 | end 168 | end 169 | 170 | -- locale independent num2str and str2num functions 171 | local decpoint, numfilter 172 | 173 | local function updatedecpoint () 174 | decpoint = strmatch(tostring(0.5), "([^05+])") 175 | -- build a filter that can be used to remove group separators 176 | numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+" 177 | end 178 | 179 | updatedecpoint() 180 | 181 | local function num2str (num) 182 | return replace(fsub(tostring(num), numfilter, ""), decpoint, ".") 183 | end 184 | 185 | local function str2num (str) 186 | local num = tonumber(replace(str, ".", decpoint)) 187 | if not num then 188 | updatedecpoint() 189 | num = tonumber(replace(str, ".", decpoint)) 190 | end 191 | return num 192 | end 193 | 194 | local function addnewline2 (level, buffer, buflen) 195 | buffer[buflen+1] = "\n" 196 | buffer[buflen+2] = strrep (" ", level) 197 | buflen = buflen + 2 198 | return buflen 199 | end 200 | 201 | function json.addnewline (state) 202 | if state.indent then 203 | state.bufferlen = addnewline2 (state.level or 0, 204 | state.buffer, state.bufferlen or #(state.buffer)) 205 | end 206 | end 207 | 208 | local encode2 -- forward declaration 209 | 210 | local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state) 211 | local kt = type (key) 212 | if kt ~= 'string' and kt ~= 'number' then 213 | return nil, "type '" .. kt .. "' is not supported as a key by JSON." 214 | end 215 | if prev then 216 | buflen = buflen + 1 217 | buffer[buflen] = "," 218 | end 219 | if indent then 220 | buflen = addnewline2 (level, buffer, buflen) 221 | end 222 | buffer[buflen+1] = quotestring (key) 223 | buffer[buflen+2] = ":" 224 | return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state) 225 | end 226 | 227 | local function appendcustom(res, buffer, state) 228 | local buflen = state.bufferlen 229 | if type (res) == 'string' then 230 | buflen = buflen + 1 231 | buffer[buflen] = res 232 | end 233 | return buflen 234 | end 235 | 236 | local function exception(reason, value, state, buffer, buflen, defaultmessage) 237 | defaultmessage = defaultmessage or reason 238 | local handler = state.exception 239 | if not handler then 240 | return nil, defaultmessage 241 | else 242 | state.bufferlen = buflen 243 | local ret, msg = handler (reason, value, state, defaultmessage) 244 | if not ret then return nil, msg or defaultmessage end 245 | return appendcustom(ret, buffer, state) 246 | end 247 | end 248 | 249 | function json.encodeexception(reason, value, state, defaultmessage) 250 | return quotestring("<" .. defaultmessage .. ">") 251 | end 252 | 253 | encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state) 254 | local valtype = type (value) 255 | local valmeta = getmetatable (value) 256 | valmeta = type (valmeta) == 'table' and valmeta -- only tables 257 | local valtojson = valmeta and valmeta.__tojson 258 | if valtojson then 259 | if tables[value] then 260 | return exception('reference cycle', value, state, buffer, buflen) 261 | end 262 | tables[value] = true 263 | state.bufferlen = buflen 264 | local ret, msg = valtojson (value, state) 265 | if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end 266 | tables[value] = nil 267 | buflen = appendcustom(ret, buffer, state) 268 | elseif value == nil then 269 | buflen = buflen + 1 270 | buffer[buflen] = "null" 271 | elseif valtype == 'number' then 272 | local s 273 | if value ~= value or value >= huge or -value >= huge then 274 | -- This is the behaviour of the original JSON implementation. 275 | s = "null" 276 | else 277 | s = num2str (value) 278 | end 279 | buflen = buflen + 1 280 | buffer[buflen] = s 281 | elseif valtype == 'boolean' then 282 | buflen = buflen + 1 283 | buffer[buflen] = value and "true" or "false" 284 | elseif valtype == 'string' then 285 | buflen = buflen + 1 286 | buffer[buflen] = quotestring (value) 287 | elseif valtype == 'table' then 288 | if tables[value] then 289 | return exception('reference cycle', value, state, buffer, buflen) 290 | end 291 | tables[value] = true 292 | level = level + 1 293 | local isa, n = isarray (value) 294 | if n == 0 and valmeta and valmeta.__jsontype == 'object' then 295 | isa = false 296 | end 297 | local msg 298 | if isa then -- JSON array 299 | buflen = buflen + 1 300 | buffer[buflen] = "[" 301 | for i = 1, n do 302 | buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state) 303 | if not buflen then return nil, msg end 304 | if i < n then 305 | buflen = buflen + 1 306 | buffer[buflen] = "," 307 | end 308 | end 309 | buflen = buflen + 1 310 | buffer[buflen] = "]" 311 | else -- JSON object 312 | local prev = false 313 | buflen = buflen + 1 314 | buffer[buflen] = "{" 315 | local order = valmeta and valmeta.__jsonorder or globalorder 316 | if order then 317 | local used = {} 318 | n = #order 319 | for i = 1, n do 320 | local k = order[i] 321 | local v = value[k] 322 | if v then 323 | used[k] = true 324 | buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) 325 | prev = true -- add a seperator before the next element 326 | end 327 | end 328 | for k,v in pairs (value) do 329 | if not used[k] then 330 | buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) 331 | if not buflen then return nil, msg end 332 | prev = true -- add a seperator before the next element 333 | end 334 | end 335 | else -- unordered 336 | for k,v in pairs (value) do 337 | buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) 338 | if not buflen then return nil, msg end 339 | prev = true -- add a seperator before the next element 340 | end 341 | end 342 | if indent then 343 | buflen = addnewline2 (level - 1, buffer, buflen) 344 | end 345 | buflen = buflen + 1 346 | buffer[buflen] = "}" 347 | end 348 | tables[value] = nil 349 | else 350 | return exception ('unsupported type', value, state, buffer, buflen, 351 | "type '" .. valtype .. "' is not supported by JSON.") 352 | end 353 | return buflen 354 | end 355 | 356 | function json.encode (value, state) 357 | state = state or {} 358 | local oldbuffer = state.buffer 359 | local buffer = oldbuffer or {} 360 | state.buffer = buffer 361 | updatedecpoint() 362 | local ret, msg = encode2 (value, state.indent, state.level or 0, 363 | buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state) 364 | if not ret then 365 | error (msg, 2) 366 | elseif oldbuffer == buffer then 367 | state.bufferlen = ret 368 | return true 369 | else 370 | state.bufferlen = nil 371 | state.buffer = nil 372 | return concat (buffer) 373 | end 374 | end 375 | 376 | local function loc (str, where) 377 | local line, pos, linepos = 1, 1, 0 378 | while true do 379 | pos = strfind (str, "\n", pos, true) 380 | if pos and pos < where then 381 | line = line + 1 382 | linepos = pos 383 | pos = pos + 1 384 | else 385 | break 386 | end 387 | end 388 | return "line " .. line .. ", column " .. (where - linepos) 389 | end 390 | 391 | local function unterminated (str, what, where) 392 | return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where) 393 | end 394 | 395 | local function scanwhite (str, pos) 396 | while true do 397 | pos = strfind (str, "%S", pos) 398 | if not pos then return nil end 399 | local sub2 = strsub (str, pos, pos + 1) 400 | if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then 401 | -- UTF-8 Byte Order Mark 402 | pos = pos + 3 403 | elseif sub2 == "//" then 404 | pos = strfind (str, "[\n\r]", pos + 2) 405 | if not pos then return nil end 406 | elseif sub2 == "/*" then 407 | pos = strfind (str, "*/", pos + 2) 408 | if not pos then return nil end 409 | pos = pos + 2 410 | else 411 | return pos 412 | end 413 | end 414 | end 415 | 416 | local escapechars = { 417 | ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f", 418 | ["n"] = "\n", ["r"] = "\r", ["t"] = "\t" 419 | } 420 | 421 | local function unichar (value) 422 | if value < 0 then 423 | return nil 424 | elseif value <= 0x007f then 425 | return strchar (value) 426 | elseif value <= 0x07ff then 427 | return strchar (0xc0 + floor(value/0x40), 428 | 0x80 + (floor(value) % 0x40)) 429 | elseif value <= 0xffff then 430 | return strchar (0xe0 + floor(value/0x1000), 431 | 0x80 + (floor(value/0x40) % 0x40), 432 | 0x80 + (floor(value) % 0x40)) 433 | elseif value <= 0x10ffff then 434 | return strchar (0xf0 + floor(value/0x40000), 435 | 0x80 + (floor(value/0x1000) % 0x40), 436 | 0x80 + (floor(value/0x40) % 0x40), 437 | 0x80 + (floor(value) % 0x40)) 438 | else 439 | return nil 440 | end 441 | end 442 | 443 | local function scanstring (str, pos) 444 | local lastpos = pos + 1 445 | local buffer, n = {}, 0 446 | while true do 447 | local nextpos = strfind (str, "[\"\\]", lastpos) 448 | if not nextpos then 449 | return unterminated (str, "string", pos) 450 | end 451 | if nextpos > lastpos then 452 | n = n + 1 453 | buffer[n] = strsub (str, lastpos, nextpos - 1) 454 | end 455 | if strsub (str, nextpos, nextpos) == "\"" then 456 | lastpos = nextpos + 1 457 | break 458 | else 459 | local escchar = strsub (str, nextpos + 1, nextpos + 1) 460 | local value 461 | if escchar == "u" then 462 | value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16) 463 | if value then 464 | local value2 465 | if 0xD800 <= value and value <= 0xDBff then 466 | -- we have the high surrogate of UTF-16. Check if there is a 467 | -- low surrogate escaped nearby to combine them. 468 | if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then 469 | value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16) 470 | if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then 471 | value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000 472 | else 473 | value2 = nil -- in case it was out of range for a low surrogate 474 | end 475 | end 476 | end 477 | value = value and unichar (value) 478 | if value then 479 | if value2 then 480 | lastpos = nextpos + 12 481 | else 482 | lastpos = nextpos + 6 483 | end 484 | end 485 | end 486 | end 487 | if not value then 488 | value = escapechars[escchar] or escchar 489 | lastpos = nextpos + 2 490 | end 491 | n = n + 1 492 | buffer[n] = value 493 | end 494 | end 495 | if n == 1 then 496 | return buffer[1], lastpos 497 | elseif n > 1 then 498 | return concat (buffer), lastpos 499 | else 500 | return "", lastpos 501 | end 502 | end 503 | 504 | local scanvalue -- forward declaration 505 | 506 | local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta) 507 | local len = strlen (str) 508 | local tbl, n = {}, 0 509 | local pos = startpos + 1 510 | if what == 'object' then 511 | setmetatable (tbl, objectmeta) 512 | else 513 | setmetatable (tbl, arraymeta) 514 | end 515 | while true do 516 | pos = scanwhite (str, pos) 517 | if not pos then return unterminated (str, what, startpos) end 518 | local char = strsub (str, pos, pos) 519 | if char == closechar then 520 | return tbl, pos + 1 521 | end 522 | local val1, err 523 | val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) 524 | if err then return nil, pos, err end 525 | pos = scanwhite (str, pos) 526 | if not pos then return unterminated (str, what, startpos) end 527 | char = strsub (str, pos, pos) 528 | if char == ":" then 529 | if val1 == nil then 530 | return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")" 531 | end 532 | pos = scanwhite (str, pos + 1) 533 | if not pos then return unterminated (str, what, startpos) end 534 | local val2 535 | val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) 536 | if err then return nil, pos, err end 537 | tbl[val1] = val2 538 | pos = scanwhite (str, pos) 539 | if not pos then return unterminated (str, what, startpos) end 540 | char = strsub (str, pos, pos) 541 | else 542 | n = n + 1 543 | tbl[n] = val1 544 | end 545 | if char == "," then 546 | pos = pos + 1 547 | end 548 | end 549 | end 550 | 551 | scanvalue = function (str, pos, nullval, objectmeta, arraymeta) 552 | pos = pos or 1 553 | pos = scanwhite (str, pos) 554 | if not pos then 555 | return nil, strlen (str) + 1, "no valid JSON value (reached the end)" 556 | end 557 | local char = strsub (str, pos, pos) 558 | if char == "{" then 559 | return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta) 560 | elseif char == "[" then 561 | return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta) 562 | elseif char == "\"" then 563 | return scanstring (str, pos) 564 | else 565 | local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos) 566 | if pstart then 567 | local number = str2num (strsub (str, pstart, pend)) 568 | if number then 569 | return number, pend + 1 570 | end 571 | end 572 | pstart, pend = strfind (str, "^%a%w*", pos) 573 | if pstart then 574 | local name = strsub (str, pstart, pend) 575 | if name == "true" then 576 | return true, pend + 1 577 | elseif name == "false" then 578 | return false, pend + 1 579 | elseif name == "null" then 580 | return nullval, pend + 1 581 | end 582 | end 583 | return nil, pos, "no valid JSON value at " .. loc (str, pos) 584 | end 585 | end 586 | 587 | local function optionalmetatables(...) 588 | if select("#", ...) > 0 then 589 | return ... 590 | else 591 | return {__jsontype = 'object'}, {__jsontype = 'array'} 592 | end 593 | end 594 | 595 | function json.decode (str, pos, nullval, ...) 596 | local objectmeta, arraymeta = optionalmetatables(...) 597 | return scanvalue (str, pos, nullval, objectmeta, arraymeta) 598 | end 599 | 600 | function json.use_lpeg () 601 | local g = require ("lpeg") 602 | 603 | if g.version() == "0.11" then 604 | error "due to a bug in LPeg 0.11, it cannot be used for JSON matching" 605 | end 606 | 607 | local pegmatch = g.match 608 | local P, S, R = g.P, g.S, g.R 609 | 610 | local function ErrorCall (str, pos, msg, state) 611 | if not state.msg then 612 | state.msg = msg .. " at " .. loc (str, pos) 613 | state.pos = pos 614 | end 615 | return false 616 | end 617 | 618 | local function Err (msg) 619 | return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall) 620 | end 621 | 622 | local SingleLineComment = P"//" * (1 - S"\n\r")^0 623 | local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/" 624 | local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0 625 | 626 | local PlainChar = 1 - S"\"\\\n\r" 627 | local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars 628 | local HexDigit = R("09", "af", "AF") 629 | local function UTF16Surrogate (match, pos, high, low) 630 | high, low = tonumber (high, 16), tonumber (low, 16) 631 | if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then 632 | return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000) 633 | else 634 | return false 635 | end 636 | end 637 | local function UTF16BMP (hex) 638 | return unichar (tonumber (hex, 16)) 639 | end 640 | local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit)) 641 | local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP 642 | local Char = UnicodeEscape + EscapeSequence + PlainChar 643 | local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string") 644 | local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0)) 645 | local Fractal = P"." * R"09"^0 646 | local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1 647 | local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num 648 | local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1) 649 | local SimpleValue = Number + String + Constant 650 | local ArrayContent, ObjectContent 651 | 652 | -- The functions parsearray and parseobject parse only a single value/pair 653 | -- at a time and store them directly to avoid hitting the LPeg limits. 654 | local function parsearray (str, pos, nullval, state) 655 | local obj, cont 656 | local npos 657 | local t, nt = {}, 0 658 | repeat 659 | obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state) 660 | if not npos then break end 661 | pos = npos 662 | nt = nt + 1 663 | t[nt] = obj 664 | until cont == 'last' 665 | return pos, setmetatable (t, state.arraymeta) 666 | end 667 | 668 | local function parseobject (str, pos, nullval, state) 669 | local obj, key, cont 670 | local npos 671 | local t = {} 672 | repeat 673 | key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state) 674 | if not npos then break end 675 | pos = npos 676 | t[key] = obj 677 | until cont == 'last' 678 | return pos, setmetatable (t, state.objectmeta) 679 | end 680 | 681 | local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected") 682 | local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected") 683 | local Value = Space * (Array + Object + SimpleValue) 684 | local ExpectedValue = Value + Space * Err "value expected" 685 | ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() 686 | local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue) 687 | ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() 688 | local DecodeValue = ExpectedValue * g.Cp () 689 | 690 | function json.decode (str, pos, nullval, ...) 691 | local state = {} 692 | state.objectmeta, state.arraymeta = optionalmetatables(...) 693 | local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state) 694 | if state.msg then 695 | return nil, state.pos, state.msg 696 | else 697 | return obj, retpos 698 | end 699 | end 700 | 701 | -- use this function only once: 702 | json.use_lpeg = function () return json end 703 | 704 | json.using_lpeg = true 705 | 706 | return json -- so you can get the module using json = require "dkjson".use_lpeg() 707 | end 708 | 709 | if always_try_using_lpeg then 710 | pcall (json.use_lpeg) 711 | end 712 | 713 | return json 714 | 715 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------