├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE ├── README.md ├── bot ├── bot.lua └── utils.lua ├── data ├── .gitkeep └── photos │ └── .gitkeep ├── etc └── uzzbot.conf ├── launch.sh ├── libs ├── JSON.lua ├── dkjson.lua ├── mimetype.lua └── redis.lua └── plugins ├── 9gag.lua ├── banhammer.lua ├── boobs.lua ├── broadcast.lua ├── bugzilla.lua ├── calculator.lua ├── channels.lua ├── chuck_norris.lua ├── danbooru.lua ├── domaintools.lua ├── download_media.lua ├── echo.lua ├── eur.lua ├── exchange.lua ├── expand.lua ├── face.lua ├── get.lua ├── giphy.lua ├── gnuplot.lua ├── google.lua ├── gps.lua ├── groupmanager.lua ├── hackernews.lua ├── hello.lua ├── help.lua ├── id.lua ├── images.lua ├── imdb.lua ├── img_google.lua ├── invite.lua ├── isX.lua ├── isup.lua ├── join.lua ├── location.lua ├── lyrics.lua ├── magic8ball.lua ├── media.lua ├── media_handler.lua ├── meme.lua ├── minecraft.lua ├── moderation.lua ├── pili.lua ├── plugins.lua ├── pokedex.lua ├── qr.lua ├── quotes.lua ├── rae.lua ├── remind.lua ├── roll.lua ├── rss.lua ├── search_youtube.lua ├── service_entergroup.lua ├── service_template.lua ├── set.lua ├── stats.lua ├── steam.lua ├── sudo.lua ├── tex.lua ├── time.lua ├── torrent_search.lua ├── translate.lua ├── trivia.lua ├── tweet.lua ├── twitter.lua ├── twitter_send.lua ├── version.lua ├── vote.lua ├── weather.lua ├── webshot.lua ├── wiki.lua ├── xkcd.lua ├── yoda.lua └── youtube.lua /.gitignore: -------------------------------------------------------------------------------- 1 | res/ 2 | data/ 3 | .luarocks -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tg"] 2 | path = tg 3 | url = https://github.com/vysheng/tg 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | 3 | before_install: 4 | - sudo apt-get update -qq 5 | - sudo apt-get install -qq libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev make unzip git libjansson-dev python2.7-dev 6 | - ./launch.sh install 7 | 8 | script: 9 | - luac -p bot/*.lua 10 | - luac -p plugins/*.lua 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | uzzbot (telegram-bot) 2 | ============ 3 | 4 | [![Donate button](https://img.shields.io/badge/nepal-donate-yellow.svg)](http://www.nrcs.org/donate-nrcs "Donate to Nepal Red Cross Society") 5 | 6 | A Telegram Bot based on plugins using [tg](https://github.com/vysheng/tg). Forked from [Yagop's](https://github.com/yagop/telegram-bot). 7 | 8 | [Installation](https://github.com/yagop/telegram-bot/wiki/Installation) 9 | ------------ 10 | ```bash 11 | # Tested on Ubuntu 14.04, for other OSs check out https://github.com/yagop/telegram-bot/wiki/Installation 12 | sudo apt-get install libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev make unzip git redis-server g++ libjansson-dev libpython-dev expat libexpat1-dev 13 | ``` 14 | 15 | ```bash 16 | # After those dependencies, lets install the bot 17 | cd $HOME 18 | git clone https://github.com/uziins/uzzbot.git 19 | cd uzzbot 20 | ./launch.sh install 21 | ./launch.sh # Will ask you for a phone number & confirmation code. 22 | ``` 23 | 24 | Enable more [`plugins`](https://github.com/uziins/uzzbot/tree/master/plugins) 25 | ------------- 26 | See the plugins list with `!plugins` command. 27 | 28 | Enable a disabled plugin by `!plugins enable [name]`. 29 | 30 | Disable an enabled plugin by `!plugins disable [name]`. 31 | 32 | Those commands require a privileged user, privileged users are defined inside `data/config.lua` (generated by the bot), stop the bot and edit if necessary. 33 | 34 | 35 | Run it as a daemon 36 | ------------ 37 | If your Linux/Unix comes with [upstart](http://upstart.ubuntu.com/) you can run the bot by this way 38 | ```bash 39 | $ sed -i "s/yourusername/$(whoami)/g" etc/uzzbot.conf 40 | $ sed -i "s_telegrambotpath_$(pwd)_g" etc/uzzbot.conf 41 | $ sudo cp etc/uzzbot.conf /etc/init/ 42 | $ sudo start uzzbot # To start it 43 | $ sudo stop uzzbot # To stop it 44 | ``` 45 | 46 | 47 | ------------ 48 | Bot: [uzzbot](https://telegram.me/uzzbot) 49 | 50 | [Join](https://telegram.me/joinchat/ALJ3iwFAhOCh4WNUHAyzXQ) on the TelegramBot Discussion Group. 51 | or 52 | [Join](https://telegram.me/joinchat/045d20af01e2c643263fec0188be277b) for uzzbot support. 53 | -------------------------------------------------------------------------------- /bot/bot.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path .. ';.luarocks/share/lua/5.2/?.lua' 2 | ..';.luarocks/share/lua/5.2/?/init.lua' 3 | package.cpath = package.cpath .. ';.luarocks/lib/lua/5.2/?.so' 4 | 5 | require("./bot/utils") 6 | 7 | VERSION = '0.14.6' 8 | 9 | -- This function is called when tg receive a msg 10 | function on_msg_receive (msg) 11 | if not started then 12 | return 13 | end 14 | 15 | local receiver = get_receiver(msg) 16 | 17 | -- vardump(msg) 18 | msg = pre_process_service_msg(msg) 19 | if msg_valid(msg) then 20 | msg = pre_process_msg(msg) 21 | if msg then 22 | match_plugins(msg) 23 | mark_read(receiver, ok_cb, false) 24 | end 25 | end 26 | end 27 | 28 | function ok_cb(extra, success, result) 29 | end 30 | 31 | function on_binlog_replay_end() 32 | started = true 33 | postpone (cron_plugins, false, 60*5.0) 34 | -- See plugins/isup.lua as an example for cron 35 | 36 | _config = load_config() 37 | 38 | -- load plugins 39 | plugins = {} 40 | load_plugins() 41 | end 42 | 43 | function msg_valid(msg) 44 | -- Don't process outgoing messages 45 | if msg.out then 46 | print('\27[36mNot valid: msg from us\27[39m') 47 | return false 48 | end 49 | 50 | -- Before bot was started 51 | if msg.date < now then 52 | print('\27[36mNot valid: old msg\27[39m') 53 | return false 54 | end 55 | 56 | if msg.unread == 0 then 57 | print('\27[36mNot valid: readed\27[39m') 58 | return false 59 | end 60 | 61 | if not msg.to.id then 62 | print('\27[36mNot valid: To id not provided\27[39m') 63 | return false 64 | end 65 | 66 | if not msg.from.id then 67 | print('\27[36mNot valid: From id not provided\27[39m') 68 | return false 69 | end 70 | 71 | if msg.from.id == our_id then 72 | print('\27[36mNot valid: Msg from our id\27[39m') 73 | return false 74 | end 75 | 76 | if msg.to.type == 'encr_chat' then 77 | print('\27[36mNot valid: Encrypted chat\27[39m') 78 | return false 79 | end 80 | 81 | if msg.from.id == 777000 then 82 | print('\27[36mNot valid: Telegram message\27[39m') 83 | return false 84 | end 85 | 86 | return true 87 | end 88 | 89 | -- 90 | function pre_process_service_msg(msg) 91 | if msg.service then 92 | local action = msg.action or {type=""} 93 | -- Double ! to discriminate of normal actions 94 | msg.text = "!!tgservice " .. action.type 95 | 96 | -- wipe the data to allow the bot to read service messages 97 | if msg.out then 98 | msg.out = false 99 | end 100 | if msg.from.id == our_id then 101 | msg.from.id = 0 102 | end 103 | end 104 | return msg 105 | end 106 | 107 | -- Apply plugin.pre_process function 108 | function pre_process_msg(msg) 109 | for name,plugin in pairs(plugins) do 110 | if plugin.pre_process and msg then 111 | print('Preprocess', name) 112 | msg = plugin.pre_process(msg) 113 | end 114 | end 115 | 116 | return msg 117 | end 118 | 119 | -- Go over enabled plugins patterns. 120 | function match_plugins(msg) 121 | for name, plugin in pairs(plugins) do 122 | match_plugin(plugin, name, msg) 123 | end 124 | end 125 | 126 | -- Check if plugin is on _config.disabled_plugin_on_chat table 127 | local function is_plugin_disabled_on_chat(plugin_name, receiver) 128 | local disabled_chats = _config.disabled_plugin_on_chat 129 | -- Table exists and chat has disabled plugins 130 | if disabled_chats and disabled_chats[receiver] then 131 | -- Checks if plugin is disabled on this chat 132 | for disabled_plugin,disabled in pairs(disabled_chats[receiver]) do 133 | if disabled_plugin == plugin_name and disabled then 134 | if plugins[disabled_plugin].hidden then 135 | print('Plugin '..disabled_plugin..' is disabled on this chat') 136 | else 137 | local warning = 'Plugin '..disabled_plugin..' is disabled on this chat' 138 | print(warning) 139 | send_msg(receiver, warning, ok_cb, false) 140 | end 141 | return true 142 | end 143 | end 144 | end 145 | return false 146 | end 147 | 148 | function match_plugin(plugin, plugin_name, msg) 149 | local receiver = get_receiver(msg) 150 | 151 | -- Go over patterns. If one matches it's enough. 152 | for k, pattern in pairs(plugin.patterns) do 153 | local matches = match_pattern(pattern, msg.text) 154 | if matches then 155 | print("msg matches: ", pattern) 156 | 157 | if is_plugin_disabled_on_chat(plugin_name, receiver) then 158 | return nil 159 | end 160 | -- Function exists 161 | if plugin.run then 162 | -- If plugin is for privileged users only 163 | if not warns_user_not_allowed(plugin, msg) then 164 | local result = plugin.run(msg, matches) 165 | if result then 166 | send_large_msg(receiver, result) 167 | end 168 | end 169 | end 170 | -- One patterns matches 171 | return 172 | end 173 | end 174 | end 175 | 176 | -- DEPRECATED, use send_large_msg(destination, text) 177 | function _send_msg(destination, text) 178 | send_large_msg(destination, text) 179 | end 180 | 181 | -- Save the content of _config to config.lua 182 | function save_config( ) 183 | serialize_to_file(_config, './data/config.lua') 184 | print ('saved config into ./data/config.lua') 185 | end 186 | 187 | -- Returns the config from config.lua file. 188 | -- If file doesn't exist, create it. 189 | function load_config( ) 190 | local f = io.open('./data/config.lua', "r") 191 | -- If config.lua doesn't exist 192 | if not f then 193 | print ("Created new config file: data/config.lua") 194 | create_config() 195 | else 196 | f:close() 197 | end 198 | local config = loadfile ("./data/config.lua")() 199 | for v,user in pairs(config.sudo_users) do 200 | print("Allowed user: " .. user) 201 | end 202 | return config 203 | end 204 | 205 | -- Create a basic config.json file and saves it. 206 | function create_config( ) 207 | -- A simple config with basic plugins and ourselves as privileged user 208 | config = { 209 | enabled_plugins = { 210 | "echo", 211 | "get", 212 | "google", 213 | "groupmanager", 214 | "help", 215 | "id", 216 | "images", 217 | "img_google", 218 | "location", 219 | "media", 220 | "plugins", 221 | "channels", 222 | "set", 223 | "stats", 224 | "time", 225 | "version", 226 | "weather", 227 | "youtube", 228 | "media_handler", 229 | "moderation"}, 230 | sudo_users = {our_id}, 231 | disabled_channels = {}, 232 | moderation = {data = 'data/moderation.json'} 233 | } 234 | serialize_to_file(config, './data/config.lua') 235 | print ('saved config into ./data/config.lua') 236 | end 237 | 238 | function on_our_id (id) 239 | our_id = id 240 | end 241 | 242 | function on_user_update (user, what) 243 | --vardump (user) 244 | end 245 | 246 | function on_chat_update (chat, what) 247 | --vardump (chat) 248 | end 249 | 250 | function on_secret_chat_update (schat, what) 251 | --vardump (schat) 252 | end 253 | 254 | function on_get_difference_end () 255 | end 256 | 257 | -- Enable plugins in config.json 258 | function load_plugins() 259 | for k, v in pairs(_config.enabled_plugins) do 260 | print("Loading plugin", v) 261 | 262 | local ok, err = pcall(function() 263 | local t = loadfile("plugins/"..v..'.lua')() 264 | plugins[v] = t 265 | end) 266 | 267 | if not ok then 268 | print('\27[31mError loading plugin '..v..'\27[39m') 269 | print('\27[31m'..err..'\27[39m') 270 | end 271 | 272 | end 273 | end 274 | 275 | -- custom add 276 | function load_data(filename) 277 | 278 | local f = io.open(filename) 279 | if not f then 280 | return {} 281 | end 282 | local s = f:read('*all') 283 | f:close() 284 | local data = JSON.decode(s) 285 | 286 | return data 287 | 288 | end 289 | 290 | function save_data(filename, data) 291 | 292 | local s = JSON.encode(data) 293 | local f = io.open(filename, 'w') 294 | f:write(s) 295 | f:close() 296 | 297 | end 298 | 299 | -- Call and postpone execution for cron plugins 300 | function cron_plugins() 301 | 302 | for name, plugin in pairs(plugins) do 303 | -- Only plugins with cron function 304 | if plugin.cron ~= nil then 305 | plugin.cron() 306 | end 307 | end 308 | 309 | -- Called again in 5 mins 310 | postpone (cron_plugins, false, 5*60.0) 311 | end 312 | 313 | -- Start and load values 314 | our_id = 0 315 | now = os.time() 316 | math.randomseed(now) 317 | started = false 318 | -------------------------------------------------------------------------------- /data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uziins/uzzbot/ab6346c8a9ecb0c7b81b14d2abc0a6c2fe517317/data/.gitkeep -------------------------------------------------------------------------------- /data/photos/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /etc/uzzbot.conf: -------------------------------------------------------------------------------- 1 | description "uzzbot upstart script" 2 | 3 | respawn 4 | respawn limit 15 5 5 | 6 | start on runlevel [2345] 7 | stop on shutdown 8 | 9 | setuid yourusername 10 | exec /bin/sh telegrambotpath/launch.sh 11 | -------------------------------------------------------------------------------- /launch.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | THIS_DIR=$(cd $(dirname $0); pwd) 4 | cd $THIS_DIR 5 | 6 | update() { 7 | git pull 8 | git submodule update --init --recursive 9 | install_rocks 10 | } 11 | 12 | # Will install luarocks on THIS_DIR/.luarocks 13 | install_luarocks() { 14 | git clone https://github.com/keplerproject/luarocks.git 15 | cd luarocks 16 | git checkout tags/v2.2.1 # Current stable 17 | 18 | PREFIX="$THIS_DIR/.luarocks" 19 | 20 | ./configure --prefix=$PREFIX --sysconfdir=$PREFIX/luarocks --force-config 21 | 22 | RET=$?; if [ $RET -ne 0 ]; 23 | then echo "Error. Exiting."; exit $RET; 24 | fi 25 | 26 | make build && make install 27 | RET=$?; if [ $RET -ne 0 ]; 28 | then echo "Error. Exiting.";exit $RET; 29 | fi 30 | 31 | cd .. 32 | rm -rf luarocks 33 | } 34 | 35 | install_rocks() { 36 | ./.luarocks/bin/luarocks install luasocket 37 | RET=$?; if [ $RET -ne 0 ]; 38 | then echo "Error. Exiting."; exit $RET; 39 | fi 40 | 41 | ./.luarocks/bin/luarocks install oauth 42 | RET=$?; if [ $RET -ne 0 ]; 43 | then echo "Error. Exiting."; exit $RET; 44 | fi 45 | 46 | ./.luarocks/bin/luarocks install redis-lua 47 | RET=$?; if [ $RET -ne 0 ]; 48 | then echo "Error. Exiting."; exit $RET; 49 | fi 50 | 51 | ./.luarocks/bin/luarocks install lua-cjson 52 | RET=$?; if [ $RET -ne 0 ]; 53 | then echo "Error. Exiting."; exit $RET; 54 | fi 55 | 56 | ./.luarocks/bin/luarocks install fakeredis 57 | RET=$?; if [ $RET -ne 0 ]; 58 | then echo "Error. Exiting."; exit $RET; 59 | fi 60 | 61 | ./.luarocks/bin/luarocks install xml 62 | RET=$?; if [ $RET -ne 0 ]; 63 | then echo "Error. Exiting."; exit $RET; 64 | fi 65 | 66 | ./.luarocks/bin/luarocks install feedparser 67 | RET=$?; if [ $RET -ne 0 ]; 68 | then echo "Error. Exiting."; exit $RET; 69 | fi 70 | 71 | ./.luarocks/bin/luarocks install serpent 72 | RET=$?; if [ $RET -ne 0 ]; 73 | then echo "Error. Exiting."; exit $RET; 74 | fi 75 | } 76 | 77 | install() { 78 | git pull 79 | git submodule update --init --recursive 80 | cd tg && ./configure && make 81 | 82 | RET=$?; if [ $RET -ne 0 ]; then 83 | echo "Trying without Python..."; 84 | ./configure --disable-python && make 85 | RET=$? 86 | fi 87 | 88 | if [ $RET -ne 0 ]; then 89 | echo "Error. Exiting."; exit $RET; 90 | fi 91 | cd .. 92 | install_luarocks 93 | install_rocks 94 | } 95 | 96 | if [ "$1" = "install" ]; then 97 | install 98 | elif [ "$1" = "update" ]; then 99 | update 100 | else 101 | if [ ! -f ./tg/telegram.h ]; then 102 | echo "tg not found" 103 | echo "Run $0 install" 104 | exit 1 105 | fi 106 | 107 | if [ ! -f ./tg/bin/telegram-cli ]; then 108 | echo "tg binary not found" 109 | echo "Run $0 install" 110 | exit 1 111 | fi 112 | 113 | ./tg/bin/telegram-cli -k ./tg/tg-server.pub -s ./bot/bot.lua -l 1 -E 114 | fi 115 | -------------------------------------------------------------------------------- /libs/mimetype.lua: -------------------------------------------------------------------------------- 1 | -- Thanks to https://github.com/catwell/lua-toolbox/blob/master/mime.types 2 | do 3 | 4 | local mimetype = {} 5 | 6 | -- TODO: Add more? 7 | local types = { 8 | ["text/html"] = "html", 9 | ["text/css"] = "css", 10 | ["text/xml"] = "xml", 11 | ["image/gif"] = "gif", 12 | ["image/jpeg"] = "jpg", 13 | ["application/x-javascript"] = "js", 14 | ["application/atom+xml"] = "atom", 15 | ["application/rss+xml"] = "rss", 16 | ["text/mathml"] = "mml", 17 | ["text/plain"] = "txt", 18 | ["text/vnd.sun.j2me.app-descriptor"] = "jad", 19 | ["text/vnd.wap.wml"] = "wml", 20 | ["text/x-component"] = "htc", 21 | ["image/png"] = "png", 22 | ["image/tiff"] = "tiff", 23 | ["image/vnd.wap.wbmp"] = "wbmp", 24 | ["image/x-icon"] = "ico", 25 | ["image/x-jng"] = "jng", 26 | ["image/x-ms-bmp"] = "bmp", 27 | ["image/svg+xml"] = "svg", 28 | ["image/webp"] = "webp", 29 | ["application/java-archive"] = "jar", 30 | ["application/mac-binhex40"] = "hqx", 31 | ["application/msword"] = "doc", 32 | ["application/pdf"] = "pdf", 33 | ["application/postscript"] = "ps", 34 | ["application/rtf"] = "rtf", 35 | ["application/vnd.ms-excel"] = "xls", 36 | ["application/vnd.ms-powerpoint"] = "ppt", 37 | ["application/vnd.wap.wmlc"] = "wmlc", 38 | ["application/vnd.google-earth.kml+xml"] = "kml", 39 | ["application/vnd.google-earth.kmz"] = "kmz", 40 | ["application/x-7z-compressed"] = "7z", 41 | ["application/x-cocoa"] = "cco", 42 | ["application/x-java-archive-diff"] = "jardiff", 43 | ["application/x-java-jnlp-file"] = "jnlp", 44 | ["application/x-makeself"] = "run", 45 | ["application/x-perl"] = "pl", 46 | ["application/x-pilot"] = "prc", 47 | ["application/x-rar-compressed"] = "rar", 48 | ["application/x-redhat-package-manager"] = "rpm", 49 | ["application/x-sea"] = "sea", 50 | ["application/x-shockwave-flash"] = "swf", 51 | ["application/x-stuffit"] = "sit", 52 | ["application/x-tcl"] = "tcl", 53 | ["application/x-x509-ca-cert"] = "crt", 54 | ["application/x-xpinstall"] = "xpi", 55 | ["application/xhtml+xml"] = "xhtml", 56 | ["application/zip"] = "zip", 57 | ["application/octet-stream"] = "bin", 58 | ["audio/midi"] = "mid", 59 | ["audio/mpeg"] = "mp3", 60 | ["audio/ogg"] = "ogg", 61 | ["audio/x-m4a"] = "m4a", 62 | ["audio/x-realaudio"] = "ra", 63 | ["video/3gpp"] = "3gpp", 64 | ["video/mp4"] = "mp4", 65 | ["video/mpeg"] = "mpeg", 66 | ["video/quicktime"] = "mov", 67 | ["video/webm"] = "webm", 68 | ["video/x-flv"] = "flv", 69 | ["video/x-m4v"] = "m4v", 70 | ["video/x-mng"] = "mng", 71 | ["video/x-ms-asf"] = "asf", 72 | ["video/x-ms-wmv"] = "wmv", 73 | ["video/x-msvideo"] = "avi" 74 | } 75 | 76 | -- Returns the common file extension from a content-type 77 | function mimetype.get_mime_extension(content_type) 78 | return types[content_type] 79 | end 80 | 81 | -- Returns the mimetype and subtype 82 | function mimetype.get_content_type(extension) 83 | for k,v in pairs(types) do 84 | if v == extension then 85 | return k 86 | end 87 | end 88 | end 89 | 90 | -- Returns the mimetype without the subtype 91 | function mimetype.get_content_type_no_sub(extension) 92 | for k,v in pairs(types) do 93 | if v == extension then 94 | -- Before / 95 | return k:match('([%w-]+)/') 96 | end 97 | end 98 | end 99 | 100 | return mimetype 101 | end -------------------------------------------------------------------------------- /libs/redis.lua: -------------------------------------------------------------------------------- 1 | local Redis = require 'redis' 2 | local FakeRedis = require 'fakeredis' 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 -------------------------------------------------------------------------------- /plugins/9gag.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local function get_9GAG() 4 | local url = "http://api-9gag.herokuapp.com/" 5 | local b,c = http.request(url) 6 | if c ~= 200 then return nil end 7 | local gag = json:decode(b) 8 | -- random max json table size 9 | local i = math.random(#gag) 10 | local link_image = gag[i].src 11 | local title = gag[i].title 12 | if link_image:sub(0,2) == '//' then 13 | link_image = msg.text:sub(3,-1) 14 | end 15 | return link_image, title 16 | end 17 | 18 | local function send_title(cb_extra, success, result) 19 | if success then 20 | send_msg(cb_extra[1], cb_extra[2], ok_cb, false) 21 | end 22 | end 23 | 24 | local function run(msg, matches) 25 | local receiver = get_receiver(msg) 26 | local url, title = get_9GAG() 27 | send_photo_from_url(receiver, url, send_title, {receiver, title}) 28 | return false 29 | end 30 | 31 | return { 32 | description = "9GAG for Telegram", 33 | usage = "!9gag: Send random image from 9gag", 34 | patterns = {"^!9gag$"}, 35 | run = run 36 | } 37 | 38 | end -------------------------------------------------------------------------------- /plugins/banhammer.lua: -------------------------------------------------------------------------------- 1 | local function is_user_whitelisted(id) 2 | local hash = 'whitelist:user#id'..id 3 | local white = redis:get(hash) or false 4 | return white 5 | end 6 | 7 | local function is_chat_whitelisted(id) 8 | local hash = 'whitelist:chat#id'..id 9 | local white = redis:get(hash) or false 10 | return white 11 | end 12 | 13 | local function kick_user(user_id, chat_id) 14 | local chat = 'chat#id'..chat_id 15 | local user = 'user#id'..user_id 16 | chat_del_user(chat, user, ok_cb, true) 17 | end 18 | 19 | local function ban_user(user_id, chat_id) 20 | -- Save to redis 21 | local hash = 'banned:'..chat_id..':'..user_id 22 | redis:set(hash, true) 23 | -- Kick from chat 24 | kick_user(user_id, chat_id) 25 | end 26 | 27 | local function superban_user(user_id, chat_id) 28 | -- Save to redis 29 | local hash = 'superbanned:'..user_id 30 | redis:set(hash, true) 31 | -- Kick from chat 32 | kick_user(user_id, chat_id) 33 | end 34 | 35 | local function is_banned(user_id, chat_id) 36 | local hash = 'banned:'..chat_id..':'..user_id 37 | local banned = redis:get(hash) 38 | return banned or false 39 | end 40 | 41 | local function is_super_banned(user_id) 42 | local hash = 'superbanned:'..user_id 43 | local superbanned = redis:get(hash) 44 | return superbanned or false 45 | end 46 | 47 | local function pre_process(msg) 48 | 49 | -- SERVICE MESSAGE 50 | if msg.action and msg.action.type then 51 | local action = msg.action.type 52 | -- Check if banned user joins chat 53 | if action == 'chat_add_user' or action == 'chat_add_user_link' then 54 | local user_id 55 | if msg.action.link_issuer then 56 | user_id = msg.from.id 57 | else 58 | user_id = msg.action.user.id 59 | end 60 | print('Checking invited user '..user_id) 61 | local superbanned = is_super_banned(user_id) 62 | local banned = is_banned(user_id, msg.to.id) 63 | if superbanned or banned then 64 | print('User is banned!') 65 | kick_user(user_id, msg.to.id) 66 | end 67 | end 68 | -- No further checks 69 | return msg 70 | end 71 | 72 | -- BANNED USER TALKING 73 | if msg.to.type == 'chat' then 74 | local user_id = msg.from.id 75 | local chat_id = msg.to.id 76 | local superbanned = is_super_banned(user_id) 77 | local banned = is_banned(user_id, chat_id) 78 | if superbanned then 79 | print('SuperBanned user talking!') 80 | superban_user(user_id, chat_id) 81 | msg.text = '' 82 | end 83 | if banned then 84 | print('Banned user talking!') 85 | ban_user(user_id, chat_id) 86 | msg.text = '' 87 | end 88 | end 89 | 90 | -- WHITELIST 91 | local hash = 'whitelist:enabled' 92 | local whitelist = redis:get(hash) 93 | local issudo = is_sudo(msg) 94 | 95 | -- Allow all sudo users even if whitelist is allowed 96 | if whitelist and not issudo then 97 | print('Whitelist enabled and not sudo') 98 | -- Check if user or chat is whitelisted 99 | local allowed = is_user_whitelisted(msg.from.id) 100 | 101 | if not allowed then 102 | print('User '..msg.from.id..' not whitelisted') 103 | if msg.to.type == 'chat' then 104 | allowed = is_chat_whitelisted(msg.to.id) 105 | if not allowed then 106 | print ('Chat '..msg.to.id..' not whitelisted') 107 | else 108 | print ('Chat '..msg.to.id..' whitelisted :)') 109 | end 110 | end 111 | else 112 | print('User '..msg.from.id..' allowed :)') 113 | end 114 | 115 | if not allowed then 116 | msg.text = '' 117 | end 118 | 119 | else 120 | print('Whitelist not enabled or is sudo') 121 | end 122 | 123 | return msg 124 | end 125 | 126 | local function username_id(cb_extra, success, result) 127 | local get_cmd = cb_extra.get_cmd 128 | local receiver = cb_extra.receiver 129 | local chat_id = cb_extra.chat_id 130 | local member = cb_extra.member 131 | local text = 'No user @'..member..' in this group.' 132 | for k,v in pairs(result.members) do 133 | vusername = v.username 134 | if vusername == member then 135 | member_username = member 136 | member_id = v.id 137 | if get_cmd == 'kick' then 138 | return kick_user(member_id, chat_id) 139 | elseif get_cmd == 'ban user' then 140 | send_large_msg(receiver, 'User @'..member..' ['..member_id..'] banned') 141 | return ban_user(member_id, chat_id) 142 | elseif get_cmd == 'superban user' then 143 | send_large_msg(receiver, 'User @'..member..' ['..member_id..'] globally banned!') 144 | return superban_user(member_id, chat_id) 145 | elseif get_cmd == 'whitelist user' then 146 | local hash = 'whitelist:user#id'..member_id 147 | redis:set(hash, true) 148 | return send_large_msg(receiver, 'User @'..member..' ['..member_id..'] whitelisted') 149 | elseif get_cmd == 'whitelist delete user' then 150 | local hash = 'whitelist:user#id'..member_id 151 | redis:del(hash) 152 | return send_large_msg(receiver, 'User @'..member..' ['..member_id..'] removed from whitelist') 153 | end 154 | end 155 | end 156 | return send_large_msg(receiver, text) 157 | end 158 | 159 | local function run(msg, matches) 160 | if matches[1] == 'kickme' then 161 | kick_user(msg.from.id, msg.to.id) 162 | end 163 | if not is_momod(msg) then 164 | return nil 165 | end 166 | local receiver = get_receiver(msg) 167 | if matches[4] then 168 | get_cmd = matches[1]..' '..matches[2]..' '..matches[3] 169 | elseif matches[3] then 170 | get_cmd = matches[1]..' '..matches[2] 171 | else 172 | get_cmd = matches[1] 173 | end 174 | 175 | if matches[1] == 'ban' then 176 | local user_id = matches[3] 177 | local chat_id = msg.to.id 178 | if msg.to.type == 'chat' then 179 | if matches[2] == 'user' then 180 | if string.match(matches[3], '^%d+$') then 181 | ban_user(user_id, chat_id) 182 | send_large_msg(receiver, 'User '..user_id..' banned!') 183 | else 184 | local member = string.gsub(matches[3], '@', '') 185 | chat_info(receiver, username_id, {get_cmd=get_cmd, receiver=receiver, chat_id=chat_id, member=member}) 186 | end 187 | end 188 | if matches[2] == 'delete' then 189 | local hash = 'banned:'..chat_id..':'..user_id 190 | redis:del(hash) 191 | return 'User '..user_id..' unbanned' 192 | end 193 | else 194 | return 'This isn\'t a chat group' 195 | end 196 | end 197 | 198 | if matches[1] == 'superban' and is_admin(msg) then 199 | local user_id = matches[3] 200 | local chat_id = msg.to.id 201 | if matches[2] == 'user' then 202 | if string.match(matches[3], '^%d+$') then 203 | superban_user(user_id, chat_id) 204 | send_large_msg(receiver, 'User '..user_id..' globally banned!') 205 | else 206 | local member = string.gsub(matches[3], '@', '') 207 | chat_info(receiver, username_id, {get_cmd=get_cmd, receiver=receiver, chat_id=chat_id, member=member}) 208 | end 209 | end 210 | if matches[2] == 'delete' then 211 | local hash = 'superbanned:'..user_id 212 | redis:del(hash) 213 | return 'User '..user_id..' unbanned' 214 | end 215 | end 216 | 217 | if matches[1] == 'kick' then 218 | if msg.to.type == 'chat' then 219 | if string.match(matches[2], '^%d+$') then 220 | kick_user(matches[2], msg.to.id) 221 | else 222 | local member = string.gsub(matches[2], '@', '') 223 | chat_info(receiver, username_id, {get_cmd=get_cmd, receiver=receiver, chat_id=msg.to.id, member=member}) 224 | end 225 | else 226 | return 'This isn\'t a chat group' 227 | end 228 | end 229 | 230 | if matches[1] == 'whitelist' then 231 | if matches[2] == 'enable' and is_sudo(msg) then 232 | local hash = 'whitelist:enabled' 233 | redis:set(hash, true) 234 | return 'Enabled whitelist' 235 | end 236 | 237 | if matches[2] == 'disable' and is_sudo(msg) then 238 | local hash = 'whitelist:enabled' 239 | redis:del(hash) 240 | return 'Disabled whitelist' 241 | end 242 | 243 | if matches[2] == 'user' then 244 | if string.match(matches[3], '^%d+$') then 245 | local hash = 'whitelist:user#id'..matches[3] 246 | redis:set(hash, true) 247 | return 'User '..matches[3]..' whitelisted' 248 | else 249 | local member = string.gsub(matches[3], '@', '') 250 | chat_info(receiver, username_id, {get_cmd=get_cmd, receiver=receiver, chat_id=msg.to.id, member=member}) 251 | end 252 | end 253 | 254 | if matches[2] == 'chat' then 255 | if msg.to.type ~= 'chat' then 256 | return 'This isn\'t a chat group' 257 | end 258 | local hash = 'whitelist:chat#id'..msg.to.id 259 | redis:set(hash, true) 260 | return 'Chat '..msg.to.print_name..' ['..msg.to.id..'] whitelisted' 261 | end 262 | 263 | if matches[2] == 'delete' and matches[3] == 'user' then 264 | if string.match(matches[4], '^%d+$') then 265 | local hash = 'whitelist:user#id'..matches[4] 266 | redis:del(hash) 267 | return 'User '..matches[4]..' removed from whitelist' 268 | else 269 | local member = string.gsub(matches[4], '@', '') 270 | chat_info(receiver, username_id, {get_cmd=get_cmd, receiver=receiver, chat_id=msg.to.id, member=member}) 271 | end 272 | end 273 | 274 | if matches[2] == 'delete' and matches[3] == 'chat' then 275 | if msg.to.type ~= 'chat' then 276 | return 'This isn\'t a chat group' 277 | end 278 | local hash = 'whitelist:chat#id'..msg.to.id 279 | redis:del(hash) 280 | return 'Chat '..msg.to.print_name..' ['..msg.to.id..'] removed from whitelist' 281 | end 282 | 283 | end 284 | end 285 | 286 | return { 287 | description = "Plugin to manage bans, kicks and white/black lists.", 288 | usage = { 289 | user = "!kickme : Exit from group", 290 | moderator = { 291 | "!whitelist / : Enable or disable whitelist mode", 292 | "!whitelist user : Allow user to use the bot when whitelist mode is enabled", 293 | "!whitelist user : Allow user to use the bot when whitelist mode is enabled", 294 | "!whitelist chat : Allow everybody on current chat to use the bot when whitelist mode is enabled", 295 | "!whitelist delete user : Remove user from whitelist", 296 | "!whitelist delete chat : Remove chat from whitelist", 297 | "!ban user : Kick user from chat and kicks it if joins chat again", 298 | "!ban user : Kick user from chat and kicks it if joins chat again", 299 | "!ban delete : Unban user", 300 | "!kick : Kick user from chat group by id", 301 | "!kick : Kick user from chat group by username", 302 | }, 303 | admin = { 304 | "!superban user : Kick user from all chat and kicks it if joins again", 305 | "!superban user : Kick user from all chat and kicks it if joins again", 306 | "!superban delete : Unban user", 307 | }, 308 | }, 309 | patterns = { 310 | "^!(whitelist) (enable)$", 311 | "^!(whitelist) (disable)$", 312 | "^!(whitelist) (user) (.*)$", 313 | "^!(whitelist) (chat)$", 314 | "^!(whitelist) (delete) (user) (.*)$", 315 | "^!(whitelist) (delete) (chat)$", 316 | "^!(ban) (user) (.*)$", 317 | "^!(ban) (delete) (.*)$", 318 | "^!(superban) (user) (.*)$", 319 | "^!(superban) (delete) (.*)$", 320 | "^!(kick) (.*)$", 321 | "^!(kickme)$", 322 | "^!!tgservice (.+)$", 323 | }, 324 | run = run, 325 | pre_process = pre_process 326 | } -------------------------------------------------------------------------------- /plugins/boobs.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | -- Recursive function 4 | local function getRandomButts(attempt) 5 | attempt = attempt or 0 6 | attempt = attempt + 1 7 | 8 | local res,status = http.request("http://api.obutts.ru/noise/1") 9 | 10 | if status ~= 200 then return nil end 11 | local data = json:decode(res)[1] 12 | 13 | -- The OpenBoobs API sometimes returns an empty array 14 | if not data and attempt <= 3 then 15 | print('Cannot get that butts, trying another one...') 16 | return getRandomButts(attempt) 17 | end 18 | 19 | return 'http://media.obutts.ru/' .. data.preview 20 | end 21 | 22 | local function getRandomBoobs(attempt) 23 | attempt = attempt or 0 24 | attempt = attempt + 1 25 | 26 | local res,status = http.request("http://api.oboobs.ru/noise/1") 27 | 28 | if status ~= 200 then return nil end 29 | local data = json:decode(res)[1] 30 | 31 | -- The OpenBoobs API sometimes returns an empty array 32 | if not data and attempt < 10 then 33 | print('Cannot get that boobs, trying another one...') 34 | return getRandomBoobs(attempt) 35 | end 36 | 37 | return 'http://media.oboobs.ru/' .. data.preview 38 | end 39 | 40 | local function run(msg, matches) 41 | local url = nil 42 | 43 | if matches[1] == "!boobs" then 44 | url = getRandomBoobs() 45 | end 46 | 47 | if matches[1] == "!butts" then 48 | url = getRandomButts() 49 | end 50 | 51 | if url ~= nil then 52 | local receiver = get_receiver(msg) 53 | send_photo_from_url(receiver, url) 54 | else 55 | return 'Error getting boobs/butts for you, please try again later.' 56 | end 57 | end 58 | 59 | return { 60 | description = "Gets a random boobs or butts pic", 61 | usage = { 62 | "!boobs: Get a boobs NSFW image. 🔞", 63 | "!butts: Get a butts NSFW image. 🔞" 64 | }, 65 | patterns = { 66 | "^!boobs$", 67 | "^!butts$" 68 | }, 69 | run = run 70 | } 71 | 72 | end 73 | -------------------------------------------------------------------------------- /plugins/broadcast.lua: -------------------------------------------------------------------------------- 1 | local function returnids(cb_extra, success, result) 2 | local receiver = cb_extra.receiver 3 | local chat_id = result.id 4 | local chatname = result.print_name 5 | for k,v in pairs(result.members) do 6 | send_large_msg(v.print_name, text) 7 | end 8 | send_large_msg(receiver, 'Message broadcasted succesfully') 9 | end 10 | 11 | local function run(msg, matches) 12 | local receiver = get_receiver(msg) 13 | if not is_chat_msg(msg) then 14 | return 'Broadcast only works on group' 15 | end 16 | if matches[1] then 17 | text = 'Message for all member of ' .. string.gsub(msg.to.print_name, '_', ' ') .. ' :' 18 | text = text .. '\n\n' .. matches[1] 19 | local chat = get_receiver(msg) 20 | chat_info(chat, returnids, {receiver=receiver}) 21 | end 22 | end 23 | 24 | return { 25 | description = "Broadcast message to all group participant.", 26 | usage = { 27 | "!broadcast ", 28 | }, 29 | patterns = { 30 | "^!broadcast +(.+)$" 31 | }, 32 | run = run, 33 | moderated = true 34 | } 35 | -------------------------------------------------------------------------------- /plugins/bugzilla.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local BASE_URL = "https://bugzilla.mozilla.org/rest/" 4 | 5 | local function bugzilla_login() 6 | local url = BASE_URL.."login?login=" .. _config.bugzilla.username .. "&password=" .. _config.bugzilla.password 7 | print("accessing " .. url) 8 | local res,code = https.request( url ) 9 | local data = json:decode(res) 10 | return data 11 | end 12 | 13 | local function bugzilla_check(id) 14 | -- data = bugzilla_login() 15 | local url = BASE_URL.."bug/" .. id .. "?api_key=" .. _config.bugzilla.apikey 16 | -- print(url) 17 | local res,code = https.request( url ) 18 | local data = json:decode(res) 19 | return data 20 | end 21 | 22 | local function bugzilla_listopened(email) 23 | local url = BASE_URL.."bug?include_fields=id,summary,status,whiteboard,resolution&email1=" .. email .. "&email2=" .. email .. "&emailassigned_to2=1&emailreporter1=1&emailtype1=substring&emailtype2=substring&f1=bug_status&f2=bug_status&n1=1&n2=1&o1=equals&o2=equals&resolution=---&v1=closed&v2=resolved&api_key=" .. _config.bugzilla.apikey 24 | local res,code = https.request( url ) 25 | print(res) 26 | local data = json:decode(res) 27 | return data 28 | end 29 | 30 | local function run(msg, matches) 31 | 32 | local response = "" 33 | 34 | if matches[1] == "status" then 35 | local data = bugzilla_check(matches[2]) 36 | vardump(data) 37 | if data.error == true then 38 | return "Sorry, API failed with message: " .. data.message 39 | else 40 | response = "Bug #"..matches[1]..":\nReporter: "..data.bugs[1].creator 41 | response = response .. "\n Last update: "..data.bugs[1].last_change_time 42 | response = response .. "\n Status: "..data.bugs[1].status.." "..data.bugs[1].resolution 43 | response = response .. "\n Whiteboard: "..data.bugs[1].whiteboard 44 | response = response .. "\n Access: https://bugzilla.mozilla.org/show_bug.cgi?id=" .. matches[1] 45 | print(response) 46 | end 47 | elseif matches[1] == "list" then 48 | local data = bugzilla_listopened(matches[2]) 49 | 50 | vardump(data) 51 | if data.error == true then 52 | return "Sorry, API failed with message: " .. data.message 53 | else 54 | 55 | -- response = "Bug #"..matches[1]..":\nReporter: "..data.bugs[1].creator 56 | -- response = response .. "\n Last update: "..data.bugs[1].last_change_time 57 | -- response = response .. "\n Status: "..data.bugs[1].status.." "..data.bugs[1].resolution 58 | -- response = response .. "\n Whiteboard: "..data.bugs[1].whiteboard 59 | -- response = response .. "\n Access: https://bugzilla.mozilla.org/show_bug.cgi?id=" .. matches[1] 60 | local total = table.map_length(data.bugs) 61 | 62 | print("total bugs: " .. total) 63 | local response = "There are " .. total .. " number of bug(s) assigned/reported by " .. matches[2] 64 | 65 | if total > 0 then 66 | response = response .. ": " 67 | 68 | for tableKey, bug in pairs(data.bugs) do 69 | response = response .. "\n #" .. bug.id 70 | response = response .. "\n Status: " .. bug.status .. " " .. bug.resolution 71 | response = response .. "\n Whiteboard: " .. bug.whiteboard 72 | response = response .. "\n Summary: " .. bug.summary 73 | end 74 | end 75 | end 76 | 77 | end 78 | return response 79 | end 80 | 81 | -- (table) 82 | -- [bugs] = (table) 83 | -- [1] = (table) 84 | -- [status] = (string) ASSIGNED 85 | -- [id] = (number) 927704 86 | -- [whiteboard] = (string) [approved][full processed] 87 | -- [summary] = (string) Budget Request - Arief Bayu Purwanto - https://reps.mozilla.org/e/mozilla-summit-2013/ 88 | -- [2] = (table) 89 | -- [status] = (string) ASSIGNED 90 | -- [id] = (number) 1049337 91 | -- [whiteboard] = (string) [approved][full processed][waiting receipts][waiting report and photos] 92 | -- [summary] = (string) Budget Request - Arief Bayu Purwanto - https://reps.mozilla.org/e/workshop-firefox-os-pada-workshop-media-sosial-untuk-perubahan-1/ 93 | -- total bugs: 2 94 | 95 | return { 96 | description = "Lookup bugzilla status update", 97 | usage = "/bot bugzilla [bug number]", 98 | patterns = { 99 | "^/bugzilla (status) (.*)$", 100 | "^/bugzilla (list) (.*)$" 101 | }, 102 | run = run 103 | } 104 | 105 | end -------------------------------------------------------------------------------- /plugins/calculator.lua: -------------------------------------------------------------------------------- 1 | -- Function reference: http://mathjs.org/docs/reference/functions/categorical.html 2 | 3 | local function mathjs(exp) 4 | local url = 'http://api.mathjs.org/v1/' 5 | url = url..'?expr='..URL.escape(exp) 6 | local b,c = http.request(url) 7 | local text = nil 8 | if c == 200 then 9 | text = 'Result: '..b 10 | 11 | elseif c == 400 then 12 | text = b 13 | else 14 | text = 'Unexpected error\n' 15 | ..'Is api.mathjs.org up?' 16 | end 17 | return text 18 | end 19 | 20 | local function run(msg, matches) 21 | return mathjs(matches[1]) 22 | end 23 | 24 | return { 25 | description = "Calculate math expressions with mathjs API", 26 | usage = "!calc [expression]: evaluates the expression and sends the result.", 27 | patterns = { 28 | "^!calc (.*)$" 29 | }, 30 | run = run 31 | } 32 | -------------------------------------------------------------------------------- /plugins/channels.lua: -------------------------------------------------------------------------------- 1 | -- Checks if bot was disabled on specific chat 2 | local function is_channel_disabled( receiver ) 3 | if not _config.disabled_channels then 4 | return false 5 | end 6 | 7 | if _config.disabled_channels[receiver] == nil then 8 | return false 9 | end 10 | 11 | return _config.disabled_channels[receiver] 12 | end 13 | 14 | local function enable_channel(receiver) 15 | if not _config.disabled_channels then 16 | _config.disabled_channels = {} 17 | end 18 | 19 | if _config.disabled_channels[receiver] == nil then 20 | return 'Channel isn\'t disabled' 21 | end 22 | 23 | _config.disabled_channels[receiver] = false 24 | 25 | save_config() 26 | return "Channel re-enabled" 27 | end 28 | 29 | local function disable_channel( receiver ) 30 | if not _config.disabled_channels then 31 | _config.disabled_channels = {} 32 | end 33 | 34 | _config.disabled_channels[receiver] = true 35 | 36 | save_config() 37 | return "Channel disabled" 38 | end 39 | 40 | local function pre_process(msg) 41 | local receiver = get_receiver(msg) 42 | 43 | -- If sender is moderator then re-enable the channel 44 | --if is_sudo(msg) then 45 | if is_momod(msg) then 46 | if msg.text == "!channel enable" then 47 | enable_channel(receiver) 48 | end 49 | end 50 | 51 | if is_channel_disabled(receiver) then 52 | msg.text = "" 53 | end 54 | 55 | return msg 56 | end 57 | 58 | local function run(msg, matches) 59 | local receiver = get_receiver(msg) 60 | -- Enable a channel 61 | if matches[1] == 'enable' then 62 | return enable_channel(receiver) 63 | end 64 | -- Disable a channel 65 | if matches[1] == 'disable' then 66 | return disable_channel(receiver) 67 | end 68 | end 69 | 70 | return { 71 | description = "Plugin to manage channels. Enable or disable channel.", 72 | usage = { 73 | "!channel enable: enable current channel", 74 | "!channel disable: disable current channel" }, 75 | patterns = { 76 | "^!channel? (enable)", 77 | "^!channel? (disable)" }, 78 | run = run, 79 | --privileged = true, 80 | moderated = true, 81 | pre_process = pre_process 82 | } 83 | -------------------------------------------------------------------------------- /plugins/chuck_norris.lua: -------------------------------------------------------------------------------- 1 | local function chuck() 2 | local random = http.request("http://api.icndb.com/jokes/random") 3 | local decode = json:decode(random) 4 | local joke = decode.value.joke 5 | return joke 6 | end 7 | 8 | local function run(msg) 9 | local joke = chuck() 10 | return unescape_html(joke) 11 | end 12 | 13 | return { 14 | description = "Get random Chuck Norris jokes.", 15 | usage = "!chuck", 16 | patterns = { 17 | "^!chuck$" 18 | }, 19 | run = run 20 | } -------------------------------------------------------------------------------- /plugins/danbooru.lua: -------------------------------------------------------------------------------- 1 | do 2 | local URL = "http://danbooru.donmai.us" 3 | local URL_NEW = "/posts.json" 4 | local URL_POP = "/explore/posts/popular.json" 5 | 6 | local scale_day = "?scale=day" 7 | local scale_week = "?scale=week" 8 | local scale_month = "?scale=month" 9 | 10 | local function get_post(url) 11 | local b, c, h = http.request(url) 12 | if c ~= 200 then return nil end 13 | local posts = json:decode(b) 14 | 15 | return posts[math.random(#posts)] 16 | end 17 | 18 | local function run(msg, matches) 19 | 20 | local url = URL 21 | 22 | if matches[1] == "!danbooru" then 23 | url = url .. URL_NEW 24 | else 25 | url = url .. URL_POP 26 | 27 | if matches[1] == "d" then 28 | url = url .. scale_day 29 | elseif matches[1] == "w" then 30 | url = url .. scale_week 31 | elseif matches[1] == "m" then 32 | url = url .. scale_month 33 | end 34 | end 35 | 36 | local post = get_post(url) 37 | 38 | if post then 39 | vardump(post) 40 | local img = URL .. post.large_file_url 41 | send_photo_from_url(get_receiver(msg), img) 42 | 43 | local txt = '' 44 | if post.tag_string_artist ~= '' then 45 | txt = 'Artist: ' .. post.tag_string_artist .. '\n' 46 | end 47 | if post.tag_string_character ~= '' then 48 | txt = txt .. 'Character: ' .. post.tag_string_character .. '\n' 49 | end 50 | if post.file_size ~= '' then 51 | txt = txt .. '[' .. math.ceil(post.file_size/1000) .. 'kb] ' .. URL .. post.file_url 52 | end 53 | return txt 54 | end 55 | end 56 | 57 | return { 58 | description = "Gets a random fresh or popular image from Danbooru", 59 | usage = { 60 | "!danbooru - gets a random fresh image from Danbooru 🔞", 61 | "!danboorud - random daily popular image 🔞", 62 | "!danbooruw - random weekly popular image 🔞", 63 | "!danboorum - random monthly popular image 🔞" 64 | }, 65 | patterns = { 66 | "^!danbooru$", 67 | "^!danbooru ?(d)$", 68 | "^!danbooru ?(w)$", 69 | "^!danbooru ?(m)$" 70 | }, 71 | run = run 72 | } 73 | 74 | end -------------------------------------------------------------------------------- /plugins/domaintools.lua: -------------------------------------------------------------------------------- 1 | local ltn12 = require "ltn12" 2 | local https = require "ssl.https" 3 | 4 | -- Edit data/mashape.lua with your Mashape API key 5 | -- http://docs.mashape.com/api-keys 6 | 7 | local mashape = load_from_file('data/mashape.lua', { 8 | api_key = '' 9 | }) 10 | 11 | local function check(name) 12 | local api = "https://domainsearch.p.mashape.com/index.php?" 13 | local param = "name="..name 14 | local url = api..param 15 | local api_key = mashape.api_key 16 | if api_key:isempty() then 17 | return 'Configure your Mashape API Key' 18 | end 19 | local headers = { 20 | ["X-Mashape-Key"] = api_key, 21 | ["Accept"] = "application/json" 22 | } 23 | 24 | local respbody = {} 25 | local body, code = https.request{ 26 | url = url, 27 | method = "GET", 28 | headers = headers, 29 | sink = ltn12.sink.table(respbody), 30 | protocol = "tlsv1" 31 | } 32 | if code ~= 200 then return code end 33 | local body = table.concat(respbody) 34 | local body = json:decode(body) 35 | --vardump(body) 36 | local domains = "List of domains for '"..name.."':\n" 37 | for k,v in pairs(body) do 38 | print(k) 39 | local status = " ❌ " 40 | if v == "Available" then 41 | status = " ✔ " 42 | end 43 | domains = domains..k..status.."\n" 44 | end 45 | return domains 46 | end 47 | 48 | local function run(msg, matches) 49 | if matches[1] == "check" then 50 | local name = matches[2] 51 | return check(name) 52 | end 53 | end 54 | 55 | return { 56 | description = "Domain tools", 57 | usage = {"!domain check [domain] : Check domain name availability.", 58 | }, 59 | patterns = { 60 | "^!domain (check) (.*)$", 61 | }, 62 | run = run 63 | } 64 | -------------------------------------------------------------------------------- /plugins/download_media.lua: -------------------------------------------------------------------------------- 1 | local function callback(extra, success, result) 2 | if success then 3 | print('File downloaded to:', result) 4 | else 5 | print('Error downloading: '..extra) 6 | end 7 | end 8 | 9 | local function run(msg, matches) 10 | if msg.media then 11 | if msg.media.type == 'document' then 12 | load_document(msg.id, callback, msg.id) 13 | end 14 | if msg.media.type == 'photo' then 15 | load_photo(msg.id, callback, msg.id) 16 | end 17 | if msg.media.type == 'video' then 18 | load_video(msg.id, callback, msg.id) 19 | end 20 | if msg.media.type == 'audio' then 21 | load_audio(msg.id, callback, msg.id) 22 | end 23 | end 24 | end 25 | 26 | local function pre_process(msg) 27 | if not msg.text and msg.media then 28 | msg.text = '['..msg.media.type..']' 29 | end 30 | return msg 31 | end 32 | 33 | return { 34 | description = "When bot receives a media msg, download the media.", 35 | usage = "", 36 | run = run, 37 | patterns = { 38 | '%[(document)%]', 39 | '%[(photo)%]', 40 | '%[(video)%]', 41 | '%[(audio)%]' 42 | }, 43 | pre_process = pre_process 44 | } -------------------------------------------------------------------------------- /plugins/echo.lua: -------------------------------------------------------------------------------- 1 | 2 | local function run(msg, matches) 3 | local text = matches[1] 4 | local b = 1 5 | 6 | while b ~= 0 do 7 | text = text:trim() 8 | text,b = text:gsub('^!+','') 9 | end 10 | return text 11 | end 12 | 13 | return { 14 | description = "Simplest plugin ever!", 15 | usage = "!echo [whatever]: echoes the msg", 16 | patterns = { 17 | "^!echo +(.+)$" 18 | }, 19 | run = run 20 | } 21 | -------------------------------------------------------------------------------- /plugins/eur.lua: -------------------------------------------------------------------------------- 1 | do 2 | -- TODO: More currencies 3 | 4 | -- See http://webrates.truefx.com/rates/connect.html 5 | local function getEURUSD(usd) 6 | local url = 'http://webrates.truefx.com/rates/connect.html?c=EUR/USD&f=csv&s=n' 7 | local res,code = http.request(url) 8 | local rates = res:split(", ") 9 | local symbol = rates[1] 10 | local timestamp = rates[2] 11 | local sell = rates[3]..rates[4] 12 | local buy = rates[5]..rates[6] 13 | local text = symbol..'\n'..'Buy: '..buy..'\n'..'Sell: '..sell 14 | if usd then 15 | local eur = tonumber(usd) / tonumber(buy) 16 | text = text.."\n "..usd.."USD = "..eur.."EUR" 17 | end 18 | return text 19 | end 20 | 21 | local function run(msg, matches) 22 | if matches[1] == "!eur" then 23 | return getEURUSD(nil) 24 | end 25 | return getEURUSD(matches[1]) 26 | end 27 | 28 | return { 29 | description = "Real-time EURUSD market price", 30 | usage = "!eur [USD]", 31 | patterns = { 32 | "^!eur$", 33 | "^!eur (%d+[%d%.]*)$", 34 | }, 35 | run = run 36 | } 37 | 38 | end -------------------------------------------------------------------------------- /plugins/exchange.lua: -------------------------------------------------------------------------------- 1 | local ltn12 = require "ltn12" 2 | local https = require "ssl.https" 3 | 4 | -- Edit data/mashape.lua with your Mashape API key 5 | -- http://docs.mashape.com/api-keys 6 | 7 | local function comma_value(n) -- credit http://richard.warburton.it 8 | local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$') 9 | return left..(num:reverse():gsub('(%d%d%d)','%1,'):reverse())..right 10 | end 11 | 12 | local mashape = load_from_file('data/mashape.lua', { 13 | api_key = '' 14 | }) 15 | 16 | local function request(value, from, to) 17 | local api = "https://currency-exchange.p.mashape.com/exchange?" 18 | local par1 = "from="..from 19 | local par2 = "&q="..value 20 | local par3 = "&to="..to 21 | local url = api..par1..par2..par3 22 | 23 | local api_key = mashape.api_key 24 | if api_key:isempty() then 25 | return 'Configure your Mashape API Key' 26 | end 27 | 28 | local headers = { 29 | ["X-Mashape-Key"] = api_key, 30 | ["Accept"] = "text/plain" 31 | } 32 | 33 | local respbody = {} 34 | local body, code = https.request{ 35 | url = url, 36 | method = "GET", 37 | headers = headers, 38 | sink = ltn12.sink.table(respbody), 39 | protocol = "tlsv1" 40 | } 41 | if code ~= 200 then return code end 42 | local body = table.concat(respbody) 43 | local curr = comma_value(value).." "..from.." = "..to.." "..comma_value(body) 44 | return curr 45 | end 46 | 47 | local function run(msg, matches) 48 | if tonumber(matches[1]) and not matches[2] then 49 | local from = "USD" 50 | local to = "IDR" 51 | local value = matches[1] 52 | return request(value, from, to) 53 | elseif matches[2] and matches[3] then 54 | local from = string.upper(matches[2]) or "USD" 55 | local to = string.upper(matches[3]) or "IDR" 56 | local value = matches[1] or "1" 57 | return request(value, from, to, value) 58 | end 59 | end 60 | 61 | return { 62 | description = "Currency Exchange", 63 | usage = { 64 | "!exchange [value] : Exchange value from USD to IDR (default).", 65 | "!exchange [value] [from] [to] : Get Currency Exchange by specifying the value of source (from) and destination (to).", 66 | }, 67 | patterns = { 68 | "^!exchange (%d+) (%a+) (%a+)$", 69 | "^!exchange (%d+)", 70 | }, 71 | run = run 72 | } 73 | -------------------------------------------------------------------------------- /plugins/expand.lua: -------------------------------------------------------------------------------- 1 | local function run(msg, patterns) 2 | local response_body = {} 3 | local request_constructor = { 4 | url = patterns[1], 5 | method = "HEAD", 6 | sink = ltn12.sink.table(response_body), 7 | headers = {}, 8 | redirect = false 9 | } 10 | 11 | local ok, response_code, response_headers, response_status_line = http.request(request_constructor) 12 | if ok and response_headers.location then 13 | return " 👍 " .. response_headers.location 14 | else 15 | return "Can't expand the url." 16 | end 17 | end 18 | 19 | return { 20 | description = "Expand a shortened URL to the original one.", 21 | usage = "!expand [url]: Return the original URL", 22 | patterns = { 23 | "^!expand (https?://[%w-_%.%?%.:/%+=&]+)$" 24 | }, 25 | run = run 26 | } 27 | -------------------------------------------------------------------------------- /plugins/face.lua: -------------------------------------------------------------------------------- 1 | local https = require("ssl.https") 2 | local ltn12 = require "ltn12" 3 | 4 | -- Edit data/mashape.lua with your Mashape API key 5 | -- http://docs.mashape.com/api-keys 6 | local mashape = load_from_file('data/mashape.lua', { 7 | api_key = '' 8 | }) 9 | 10 | local function request(imageUrl) 11 | local api_key = mashape.api_key 12 | if api_key:isempty() then 13 | return nil, 'Configure your Mashape API Key' 14 | end 15 | 16 | local api = "https://faceplusplus-faceplusplus.p.mashape.com/detection/detect?" 17 | local parameters = "attribute=gender%2Cage%2Crace" 18 | parameters = parameters .. "&url="..(URL.escape(imageUrl) or "") 19 | local url = api..parameters 20 | local headers = { 21 | ["X-Mashape-Key"] = api_key, 22 | ["Accept"] = "Accept: application/json" 23 | } 24 | print(url) 25 | local respbody = {} 26 | local body, code = https.request{ 27 | url = url, 28 | method = "GET", 29 | headers = headers, 30 | sink = ltn12.sink.table(respbody), 31 | protocol = "tlsv1" 32 | } 33 | if code ~= 200 then return "", code end 34 | local body = table.concat(respbody) 35 | return body, code 36 | end 37 | 38 | local function parseData(data) 39 | local jsonBody = json:decode(data) 40 | local response = "" 41 | if jsonBody.error ~= nil then 42 | if jsonBody.error == "IMAGE_ERROR_FILE_TOO_LARGE" then 43 | response = response .. "The image is too big. Provide a smaller image." 44 | elseif jsonBody.error == "IMAGE_ERROR_FAILED_TO_DOWNLOAD" then 45 | response = response .. "Is that a valid url for an image?" 46 | else 47 | response = response .. jsonBody.error 48 | end 49 | elseif jsonBody.face == nil or #jsonBody.face == 0 then 50 | response = response .. "No faces found" 51 | else 52 | response = response .. #jsonBody.face .." face(s) found:\n\n" 53 | for k,face in pairs(jsonBody.face) do 54 | local raceP = "" 55 | if face.attribute.race.confidence > 85.0 then 56 | raceP = face.attribute.race.value:lower() 57 | elseif face.attribute.race.confidence > 50.0 then 58 | raceP = "(probably "..face.attribute.race.value:lower()..")" 59 | else 60 | raceP = "(posibly "..face.attribute.race.value:lower()..")" 61 | end 62 | if face.attribute.gender.confidence > 85.0 then 63 | response = response .. "There is a " 64 | else 65 | response = response .. "There may be a " 66 | end 67 | response = response .. raceP .. " " .. face.attribute.gender.value:lower() .. " " 68 | response = response .. ", " .. face.attribute.age.value .. "(±".. face.attribute.age.range ..") years old \n" 69 | end 70 | end 71 | return response 72 | end 73 | 74 | local function run(msg, matches) 75 | --return request('http://www.uni-regensburg.de/Fakultaeten/phil_Fak_II/Psychologie/Psy_II/beautycheck/english/durchschnittsgesichter/m(01-32)_gr.jpg') 76 | local data, code = request(matches[1]) 77 | if code ~= 200 then return "There was an error. "..code end 78 | return parseData(data) 79 | end 80 | 81 | return { 82 | description = "Who is in that photo?", 83 | usage = { 84 | "!face [url]", 85 | "!recognise [url]" 86 | }, 87 | patterns = { 88 | "^!face (.*)$", 89 | "^!recognise (.*)$" 90 | }, 91 | run = run 92 | } 93 | -------------------------------------------------------------------------------- /plugins/get.lua: -------------------------------------------------------------------------------- 1 | local function get_variables_hash(msg) 2 | if msg.to.type == 'chat' then 3 | return 'chat:'..msg.to.id..':variables' 4 | end 5 | if msg.to.type == 'user' then 6 | return 'user:'..msg.from.id..':variables' 7 | end 8 | end 9 | 10 | local function list_variables(msg) 11 | local hash = get_variables_hash(msg) 12 | 13 | if hash then 14 | local names = redis:hkeys(hash) 15 | local text = '' 16 | for i=1, #names do 17 | text = text..names[i]..'\n' 18 | end 19 | return text 20 | end 21 | end 22 | 23 | local function get_value(msg, var_name) 24 | local hash = get_variables_hash(msg) 25 | if hash then 26 | local value = redis:hget(hash, var_name) 27 | if not value then 28 | return'Not found, use "!get" to list variables' 29 | else 30 | return var_name..' => '..value 31 | end 32 | end 33 | end 34 | 35 | local function run(msg, matches) 36 | if matches[2] then 37 | return get_value(msg, matches[2]) 38 | else 39 | return list_variables(msg) 40 | end 41 | end 42 | 43 | return { 44 | description = "Retrieves variables saved with !set", 45 | usage = "!get (value_name): Returns the value_name value.", 46 | patterns = { 47 | "^(!get) (.+)$", 48 | "^!get$" 49 | }, 50 | run = run 51 | } 52 | -------------------------------------------------------------------------------- /plugins/giphy.lua: -------------------------------------------------------------------------------- 1 | -- Idea by https://github.com/asdofindia/telegram-bot/ 2 | -- See http://api.giphy.com/ 3 | 4 | do 5 | 6 | local BASE_URL = 'http://api.giphy.com/v1' 7 | local API_KEY = 'dc6zaTOxFJmzC' -- public beta key 8 | 9 | local function get_image(response) 10 | local images = json:decode(response).data 11 | if #images == 0 then return nil end -- No images 12 | local i = math.random(#images) 13 | local image = images[i] -- A random one 14 | 15 | if image.images.downsized then 16 | return image.images.downsized.url 17 | end 18 | 19 | if image.images.original then 20 | return image.original.url 21 | end 22 | 23 | return nil 24 | end 25 | 26 | local function get_random_top() 27 | local url = BASE_URL.."/gifs/trending?api_key="..API_KEY 28 | local response, code = http.request(url) 29 | if code ~= 200 then return nil end 30 | return get_image(response) 31 | end 32 | 33 | local function search(text) 34 | text = URL.escape(text) 35 | local url = BASE_URL.."/gifs/search?q="..text.."&api_key="..API_KEY 36 | local response, code = http.request(url) 37 | if code ~= 200 then return nil end 38 | return get_image(response) 39 | end 40 | 41 | local function run(msg, matches) 42 | local gif_url = nil 43 | 44 | -- If no search data, a random trending GIF will be sent 45 | if matches[1] == "!gif" or matches[1] == "!giphy" then 46 | gif_url = get_random_top() 47 | else 48 | gif_url = search(matches[1]) 49 | end 50 | 51 | if not gif_url then 52 | return "Error: GIF not found" 53 | end 54 | 55 | local receiver = get_receiver(msg) 56 | print("GIF URL"..gif_url) 57 | 58 | send_document_from_url(receiver, gif_url) 59 | end 60 | 61 | return { 62 | description = "GIFs from telegram with Giphy API", 63 | usage = { 64 | "!gif (term): Search and sends GIF from Giphy. If no param, sends a trending GIF.", 65 | "!giphy (term): Search and sends GIF from Giphy. If no param, sends a trending GIF." 66 | }, 67 | patterns = { 68 | "^!gif$", 69 | "^!gif (.*)", 70 | "^!giphy (.*)", 71 | "^!giphy$" 72 | }, 73 | run = run 74 | } 75 | 76 | end 77 | -------------------------------------------------------------------------------- /plugins/gnuplot.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | * Gnuplot plugin by psykomantis 3 | * dependencies: 4 | * - gnuplot 5.00 5 | * - libgd2-xpm-dev (on Debian distr) for more info visit: https://libgd.github.io/pages/faq.html 6 | * 7 | ]] 8 | 9 | -- Gnuplot needs absolute path for the plot, so i run some commands to find where we are 10 | local outputFile = io.popen("pwd","r") 11 | io.input(outputFile) 12 | local _pwd = io.read("*line") 13 | io.close(outputFile) 14 | local _absolutePlotPath = _pwd .. "/data/plot.png" 15 | local _scriptPath = "./data/gnuplotScript.gpl" 16 | 17 | do 18 | 19 | local function gnuplot(msg, fun) 20 | local receiver = get_receiver(msg) 21 | 22 | -- We generate the plot commands 23 | local formattedString = [[ 24 | set grid 25 | set terminal png 26 | set output "]] .. _absolutePlotPath .. [[" 27 | plot ]] .. fun 28 | 29 | local file = io.open(_scriptPath,"w"); 30 | file:write(formattedString) 31 | file:close() 32 | 33 | os.execute("gnuplot " .. _scriptPath) 34 | os.remove (_scriptPath) 35 | 36 | return _send_photo(receiver, _absolutePlotPath) 37 | end 38 | 39 | -- Check all dependencies before executing 40 | local function checkDependencies() 41 | local status = os.execute("gnuplot -h") 42 | if(status==true) then 43 | status = os.execute("gnuplot -e 'set terminal png'") 44 | if(status == true) then 45 | return 0 -- OK ready to go! 46 | else 47 | return 1 -- missing libgd2-xpm-dev 48 | end 49 | else 50 | return 2 -- missing gnuplot 51 | end 52 | end 53 | 54 | local function run(msg, matches) 55 | local status = checkDependencies() 56 | if(status == 0) then 57 | return gnuplot(msg,matches[1]) 58 | elseif(status == 1) then 59 | return "It seems that this bot miss a dependency :/" 60 | else 61 | return "It seems that this bot doesn't have gnuplot :/" 62 | end 63 | end 64 | 65 | return { 66 | description = "use gnuplot through telegram, only plot single variable function", 67 | usage = "!gnuplot [single variable function]", 68 | patterns = {"^!gnuplot (.+)$"}, 69 | run = run 70 | } 71 | 72 | end 73 | -------------------------------------------------------------------------------- /plugins/google.lua: -------------------------------------------------------------------------------- 1 | local function googlethat(query) 2 | local api = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&" 3 | local parameters = "q=".. (URL.escape(query) or "") 4 | 5 | -- Do the request 6 | local res, code = https.request(api..parameters) 7 | if code ~=200 then return nil end 8 | local data = json:decode(res) 9 | 10 | local results = {} 11 | for key,result in ipairs(data.responseData.results) do 12 | table.insert(results, { 13 | result.titleNoFormatting, 14 | result.unescapedUrl or result.url 15 | }) 16 | end 17 | return results 18 | end 19 | 20 | local function stringlinks(results) 21 | local stringresults="" 22 | for key,val in ipairs(results) do 23 | stringresults=stringresults..val[1].." - "..val[2].."\n" 24 | end 25 | return stringresults 26 | end 27 | 28 | local function run(msg, matches) 29 | local results = googlethat(matches[1]) 30 | return stringlinks(results) 31 | end 32 | 33 | return { 34 | description = "Searches Google and send results", 35 | usage = "!google [terms]: Searches Google and send results", 36 | patterns = { 37 | "^!google (.*)$", 38 | "^%.[g|G]oogle (.*)$" 39 | }, 40 | run = run 41 | } 42 | -------------------------------------------------------------------------------- /plugins/gps.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | function run(msg, matches) 4 | local lat = matches[1] 5 | local lon = matches[2] 6 | local receiver = get_receiver(msg) 7 | 8 | local zooms = {16, 18} 9 | local urls = {} 10 | for i = 1, #zooms do 11 | local zoom = zooms[i] 12 | local url = "http://maps.googleapis.com/maps/api/staticmap?zoom=" .. zoom .. "&size=600x300&maptype=roadmap¢er=" .. lat .. "," .. lon .. "&markers=color:blue%7Clabel:X%7C" .. lat .. "," .. lon 13 | table.insert(urls, url) 14 | end 15 | 16 | send_photos_from_url(receiver, urls) 17 | 18 | return "www.google.es/maps/place/@" .. lat .. "," .. lon 19 | end 20 | 21 | return { 22 | description = "generates a map showing the given GPS coordinates", 23 | usage = "!gps latitude,longitude: generates a map showing the given GPS coordinates", 24 | patterns = {"^!gps ([^,]*)[,%s]([^,]*)$"}, 25 | run = run 26 | } 27 | 28 | end -------------------------------------------------------------------------------- /plugins/groupmanager.lua: -------------------------------------------------------------------------------- 1 | -- data saved to moderation.json 2 | -- check moderation plugin 3 | do 4 | 5 | local function create_group(msg) 6 | -- superuser and admins only (because sudo are always has privilege) 7 | if not is_admin(msg) then 8 | return "You're not admin!" 9 | end 10 | local group_creator = msg.from.print_name 11 | create_group_chat (group_creator, group_name, ok_cb, false) 12 | return 'Group '..string.gsub(group_name, '_', ' ')..' has been created.' 13 | end 14 | 15 | local function set_description(msg, data) 16 | if not is_momod(msg) then 17 | return "For moderators only!" 18 | end 19 | local data_cat = 'description' 20 | data[tostring(msg.to.id)][data_cat] = deskripsi 21 | save_data(_config.moderation.data, data) 22 | 23 | return 'Set group description to:\n'..deskripsi 24 | end 25 | 26 | local function get_description(msg, data) 27 | local data_cat = 'description' 28 | if not data[tostring(msg.to.id)][data_cat] then 29 | return 'No description available.' 30 | end 31 | local about = data[tostring(msg.to.id)][data_cat] 32 | local about = string.gsub(msg.to.print_name, "_", " ")..':\n\n'..about 33 | return 'About '..about 34 | end 35 | 36 | local function set_rules(msg, data) 37 | if not is_momod(msg) then 38 | return "For moderators only!" 39 | end 40 | local data_cat = 'rules' 41 | data[tostring(msg.to.id)][data_cat] = rules 42 | save_data(_config.moderation.data, data) 43 | 44 | return 'Set group rules to:\n'..rules 45 | end 46 | 47 | local function get_rules(msg, data) 48 | local data_cat = 'rules' 49 | if not data[tostring(msg.to.id)][data_cat] then 50 | return 'No rules available.' 51 | end 52 | local rules = data[tostring(msg.to.id)][data_cat] 53 | local rules = string.gsub(msg.to.print_name, '_', ' ')..' rules:\n\n'..rules 54 | return rules 55 | end 56 | 57 | -- lock/unlock group name. bot automatically change group name when locked 58 | local function lock_group_name(msg, data) 59 | if not is_momod(msg) then 60 | return "For moderators only!" 61 | end 62 | local group_name_set = data[tostring(msg.to.id)]['settings']['set_name'] 63 | local group_name_lock = data[tostring(msg.to.id)]['settings']['lock_name'] 64 | if group_name_lock == 'yes' then 65 | return 'Group name is already locked' 66 | else 67 | data[tostring(msg.to.id)]['settings']['lock_name'] = 'yes' 68 | save_data(_config.moderation.data, data) 69 | data[tostring(msg.to.id)]['settings']['set_name'] = string.gsub(msg.to.print_name, '_', ' ') 70 | save_data(_config.moderation.data, data) 71 | return 'Group name has been locked' 72 | end 73 | end 74 | 75 | local function unlock_group_name(msg, data) 76 | if not is_momod(msg) then 77 | return "For moderators only!" 78 | end 79 | local group_name_set = data[tostring(msg.to.id)]['settings']['set_name'] 80 | local group_name_lock = data[tostring(msg.to.id)]['settings']['lock_name'] 81 | if group_name_lock == 'no' then 82 | return 'Group name is already unlocked' 83 | else 84 | data[tostring(msg.to.id)]['settings']['lock_name'] = 'no' 85 | save_data(_config.moderation.data, data) 86 | return 'Group name has been unlocked' 87 | end 88 | end 89 | 90 | --lock/unlock group member. bot automatically kick new added user when locked 91 | local function lock_group_member(msg, data) 92 | if not is_momod(msg) then 93 | return "For moderators only!" 94 | end 95 | local group_member_lock = data[tostring(msg.to.id)]['settings']['lock_member'] 96 | if group_member_lock == 'yes' then 97 | return 'Group members are already locked' 98 | else 99 | data[tostring(msg.to.id)]['settings']['lock_member'] = 'yes' 100 | save_data(_config.moderation.data, data) 101 | end 102 | return 'Group members has been locked' 103 | end 104 | 105 | local function unlock_group_member(msg, data) 106 | if not is_momod(msg) then 107 | return "For moderators only!" 108 | end 109 | local group_member_lock = data[tostring(msg.to.id)]['settings']['lock_member'] 110 | if group_member_lock == 'no' then 111 | return 'Group members are not locked' 112 | else 113 | data[tostring(msg.to.id)]['settings']['lock_member'] = 'no' 114 | save_data(_config.moderation.data, data) 115 | return 'Group members has been unlocked' 116 | end 117 | end 118 | 119 | --lock/unlock group photo. bot automatically keep group photo when locked 120 | local function lock_group_photo(msg, data) 121 | if not is_momod(msg) then 122 | return "For moderators only!" 123 | end 124 | local group_photo_lock = data[tostring(msg.to.id)]['settings']['lock_photo'] 125 | if group_photo_lock == 'yes' then 126 | return 'Group photo is already locked' 127 | else 128 | data[tostring(msg.to.id)]['settings']['set_photo'] = 'waiting' 129 | save_data(_config.moderation.data, data) 130 | end 131 | return 'Please send me the group photo now' 132 | end 133 | 134 | local function unlock_group_photo(msg, data) 135 | if not is_momod(msg) then 136 | return "For moderators only!" 137 | end 138 | local group_photo_lock = data[tostring(msg.to.id)]['settings']['lock_photo'] 139 | if group_photo_lock == 'no' then 140 | return 'Group photo is not locked' 141 | else 142 | data[tostring(msg.to.id)]['settings']['lock_photo'] = 'no' 143 | save_data(_config.moderation.data, data) 144 | return 'Group photo has been unlocked' 145 | end 146 | end 147 | 148 | local function set_group_photo(msg, success, result) 149 | local data = load_data(_config.moderation.data) 150 | local receiver = get_receiver(msg) 151 | if success then 152 | local file = 'data/photos/chat_photo_'..msg.to.id..'.jpg' 153 | print('File downloaded to:', result) 154 | os.rename(result, file) 155 | print('File moved to:', file) 156 | chat_set_photo (receiver, file, ok_cb, false) 157 | data[tostring(msg.to.id)]['settings']['set_photo'] = file 158 | save_data(_config.moderation.data, data) 159 | data[tostring(msg.to.id)]['settings']['lock_photo'] = 'yes' 160 | save_data(_config.moderation.data, data) 161 | send_large_msg(receiver, 'Photo saved!', ok_cb, false) 162 | else 163 | print('Error downloading: '..msg.id) 164 | send_large_msg(receiver, 'Failed, please try again!', ok_cb, false) 165 | end 166 | end 167 | -- show group settings 168 | local function show_group_settings(msg, data) 169 | if not is_momod(msg) then 170 | return "For moderators only!" 171 | end 172 | local settings = data[tostring(msg.to.id)]['settings'] 173 | local text = "Group settings:\nLock group name : "..settings.lock_name.."\nLock group photo : "..settings.lock_photo.."\nLock group member : "..settings.lock_member 174 | return text 175 | end 176 | 177 | function run(msg, matches) 178 | --vardump(msg) 179 | if matches[1] == 'creategroup' and matches[2] then 180 | group_name = matches[2] 181 | return create_group(msg) 182 | end 183 | if not is_chat_msg(msg) then 184 | return "This is not a group chat." 185 | end 186 | local data = load_data(_config.moderation.data) 187 | local receiver = get_receiver(msg) 188 | if msg.media and is_chat_msg(msg) and is_momod(msg) then 189 | if msg.media.type == 'photo' and data[tostring(msg.to.id)] then 190 | if data[tostring(msg.to.id)]['settings']['set_photo'] == 'waiting' then 191 | load_photo(msg.id, set_group_photo, msg) 192 | end 193 | end 194 | end 195 | if data[tostring(msg.to.id)] then 196 | local settings = data[tostring(msg.to.id)]['settings'] 197 | if matches[1] == 'setabout' and matches[2] then 198 | deskripsi = matches[2] 199 | return set_description(msg, data) 200 | end 201 | if matches[1] == 'about' then 202 | return get_description(msg, data) 203 | end 204 | if matches[1] == 'setrules' then 205 | rules = matches[2] 206 | return set_rules(msg, data) 207 | end 208 | if matches[1] == 'rules' then 209 | return get_rules(msg, data) 210 | end 211 | if matches[1] == 'group' and matches[2] == 'lock' then --group lock * 212 | if matches[3] == 'name' then 213 | return lock_group_name(msg, data) 214 | end 215 | if matches[3] == 'member' then 216 | return lock_group_member(msg, data) 217 | end 218 | if matches[3] == 'photo' then 219 | return lock_group_photo(msg, data) 220 | end 221 | end 222 | if matches[1] == 'group' and matches[2] == 'unlock' then --group unlock * 223 | if matches[3] == 'name' then 224 | return unlock_group_name(msg, data) 225 | end 226 | if matches[3] == 'member' then 227 | return unlock_group_member(msg, data) 228 | end 229 | if matches[3] == 'photo' then 230 | return unlock_group_photo(msg, data) 231 | end 232 | end 233 | if matches[1] == 'group' and matches[2] == 'settings' then 234 | return show_group_settings(msg, data) 235 | end 236 | if matches[1] == 'chat_rename' then 237 | if not msg.service then 238 | return "Are you trying to troll me?" 239 | end 240 | local group_name_set = settings.set_name 241 | local group_name_lock = settings.lock_name 242 | local to_rename = 'chat#id'..msg.to.id 243 | if group_name_lock == 'yes' then 244 | if group_name_set ~= tostring(msg.to.print_name) then 245 | rename_chat(to_rename, group_name_set, ok_cb, false) 246 | end 247 | elseif group_name_lock == 'no' then 248 | return nil 249 | end 250 | end 251 | if matches[1] == 'setname' and is_momod(msg) then 252 | local new_name = string.gsub(matches[2], '_', ' ') 253 | data[tostring(msg.to.id)]['settings']['set_name'] = new_name 254 | save_data(_config.moderation.data, data) 255 | local group_name_set = data[tostring(msg.to.id)]['settings']['set_name'] 256 | local to_rename = 'chat#id'..msg.to.id 257 | rename_chat(to_rename, group_name_set, ok_cb, false) 258 | end 259 | if matches[1] == 'setphoto' and is_momod(msg) then 260 | data[tostring(msg.to.id)]['settings']['set_photo'] = 'waiting' 261 | save_data(_config.moderation.data, data) 262 | return 'Please send me new group photo now' 263 | end 264 | if matches[1] == 'chat_add_user' then 265 | if not msg.service then 266 | return "Are you trying to troll me?" 267 | end 268 | local group_member_lock = settings.lock_member 269 | local user = 'user#id'..msg.action.user.id 270 | local chat = 'chat#id'..msg.to.id 271 | if group_member_lock == 'yes' then 272 | chat_del_user(chat, user, ok_cb, true) 273 | elseif group_member_lock == 'no' then 274 | return nil 275 | end 276 | end 277 | if matches[1] == 'chat_delete_photo' then 278 | if not msg.service then 279 | return "Are you trying to troll me?" 280 | end 281 | local group_photo_lock = settings.lock_photo 282 | if group_photo_lock == 'yes' then 283 | chat_set_photo (receiver, settings.set_photo, ok_cb, false) 284 | elseif group_photo_lock == 'no' then 285 | return nil 286 | end 287 | end 288 | if matches[1] == 'chat_change_photo' and msg.from.id ~= 0 then 289 | if not msg.service then 290 | return "Are you trying to troll me?" 291 | end 292 | local group_photo_lock = settings.lock_photo 293 | if group_photo_lock == 'yes' then 294 | chat_set_photo (receiver, settings.set_photo, ok_cb, false) 295 | elseif group_photo_lock == 'no' then 296 | return nil 297 | end 298 | end 299 | end 300 | end 301 | 302 | 303 | return { 304 | description = "Plugin to manage group chat.", 305 | usage = { 306 | "!creategroup : Create a new group (admin only)", 307 | "!setabout : Set group description", 308 | "!about : Read group description", 309 | "!setrules : Set group rules", 310 | "!rules : Read group rules", 311 | "!setname : Set group name", 312 | "!setphoto : Set group photo", 313 | "!group name : Lock/unlock group name", 314 | "!group photo : Lock/unlock group photo", 315 | "!group member : Lock/unlock group member", 316 | "!group settings : Show group settings" 317 | }, 318 | patterns = { 319 | "^!(creategroup) (.*)$", 320 | "^!(setabout) (.*)$", 321 | "^!(about)$", 322 | "^!(setrules) (.*)$", 323 | "^!(rules)$", 324 | "^!(setname) (.*)$", 325 | "^!(setphoto)$", 326 | "^!(group) (lock) (.*)$", 327 | "^!(group) (unlock) (.*)$", 328 | "^!(group) (settings)$", 329 | "^!!tgservice (.+)$", 330 | "%[(photo)%]", 331 | }, 332 | run = run, 333 | } 334 | 335 | end -------------------------------------------------------------------------------- /plugins/hackernews.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | function run(msg, matches) 4 | local result = 'Hacker News Top5:\n' 5 | local top_stories_json, code = https.request('https://hacker-news.firebaseio.com/v0/topstories.json') 6 | if code ~=200 then return nil end 7 | local top_stories = json:decode(top_stories_json) 8 | for i = 1, 5 do 9 | local story_json, code = https.request('https://hacker-news.firebaseio.com/v0/item/'..top_stories[i]..'.json') 10 | if code ~=200 then return nil end 11 | local story = json:decode(story_json) 12 | result = result .. i .. '. ' .. story.title .. ' - ' .. story.url .. '\n' 13 | end 14 | return result 15 | end 16 | 17 | return { 18 | description = "Show top 5 hacker news (ycombinator.com)", 19 | usage = "!hackernews", 20 | patterns = {"^!hackernews$"}, 21 | run = run 22 | } 23 | 24 | end 25 | -------------------------------------------------------------------------------- /plugins/hello.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | function run(msg, matches) 4 | return "Hello, " .. matches[1] 5 | end 6 | 7 | return { 8 | description = "Says hello to someone", 9 | usage = "say hello to [name]", 10 | patterns = { 11 | "^say hello to (.*)$", 12 | "^Say hello to (.*)$" 13 | }, 14 | run = run 15 | } 16 | 17 | end -------------------------------------------------------------------------------- /plugins/help.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | function pairsByKeys(t, f) 4 | local a = {} 5 | for n in pairs(t) do table.insert(a, n) end 6 | table.sort(a, f) 7 | local i = 0 -- iterator variable 8 | local iter = function () -- iterator function 9 | i = i + 1 10 | if a[i] == nil then return nil 11 | else return a[i], t[a[i]] 12 | end 13 | end 14 | return iter 15 | end 16 | 17 | -- Returns true if is not empty 18 | local function has_usage_data(dict) 19 | if (dict.usage == nil or dict.usage == '') then 20 | return false 21 | end 22 | return true 23 | end 24 | 25 | -- Get commands for that plugin 26 | local function plugin_help(name,number,requester) 27 | local plugin = "" 28 | if number then 29 | local i = 0 30 | for name in pairsByKeys(plugins) do 31 | if plugins[name].hidden then 32 | name = nil 33 | else 34 | i = i + 1 35 | if i == tonumber(number) then 36 | plugin = plugins[name] 37 | end 38 | end 39 | end 40 | else 41 | plugin = plugins[name] 42 | if not plugin then return nil end 43 | end 44 | 45 | local text = "" 46 | if (type(plugin.usage) == "table") then 47 | for ku,usage in pairs(plugin.usage) do 48 | if ku == 'user' then -- usage for user 49 | if (type(plugin.usage.user) == "table") then 50 | for k,v in pairs(plugin.usage.user) do 51 | text = text..v..'\n' 52 | end 53 | elseif has_usage_data(plugin) then -- Is not empty 54 | text = text..plugin.usage.user..'\n' 55 | end 56 | elseif ku == 'moderator' then -- usage for moderator 57 | if requester == 'moderator' or requester == 'admin' or requester == 'sudo' then 58 | if (type(plugin.usage.moderator) == "table") then 59 | for k,v in pairs(plugin.usage.moderator) do 60 | text = text..v..'\n' 61 | end 62 | elseif has_usage_data(plugin) then -- Is not empty 63 | text = text..plugin.usage.moderator..'\n' 64 | end 65 | end 66 | elseif ku == 'admin' then -- usage for admin 67 | if requester == 'admin' or requester == 'sudo' then 68 | if (type(plugin.usage.admin) == "table") then 69 | for k,v in pairs(plugin.usage.admin) do 70 | text = text..v..'\n' 71 | end 72 | elseif has_usage_data(plugin) then -- Is not empty 73 | text = text..plugin.usage.admin..'\n' 74 | end 75 | end 76 | elseif ku == 'sudo' then -- usage for sudo 77 | if requester == 'sudo' then 78 | if (type(plugin.usage.sudo) == "table") then 79 | for k,v in pairs(plugin.usage.sudo) do 80 | text = text..v..'\n' 81 | end 82 | elseif has_usage_data(plugin) then -- Is not empty 83 | text = text..plugin.usage.sudo..'\n' 84 | end 85 | end 86 | else 87 | text = text..usage..'\n' 88 | end 89 | end 90 | text = text..'======================\n' 91 | elseif has_usage_data(plugin) then -- Is not empty 92 | text = text..plugin.usage..'\n======================\n' 93 | end 94 | return text 95 | end 96 | 97 | 98 | -- !help command 99 | local function telegram_help() 100 | local i = 0 101 | local text = "Plugins list:\n\n" 102 | -- Plugins names 103 | for name in pairsByKeys(plugins) do 104 | if plugins[name].hidden then 105 | name = nil 106 | else 107 | i = i + 1 108 | text = text..i..'. '..name..'\n' 109 | end 110 | end 111 | text = text..'\n'..'There are '..i..' plugins help available.' 112 | text = text..'\n'..'Write "!help [plugin name]" or "!help [plugin number]" for more info.' 113 | text = text..'\n'..'Or "!help all" to show all info.' 114 | return text 115 | end 116 | 117 | 118 | -- !help all command 119 | local function help_all(requester) 120 | local ret = "" 121 | for name in pairsByKeys(plugins) do 122 | if plugins[name].hidden then 123 | name = nil 124 | else 125 | ret = ret .. plugin_help(name, nil, requester) 126 | end 127 | end 128 | return ret 129 | end 130 | 131 | local function run(msg, matches) 132 | if is_sudo(msg) then 133 | requester = "sudo" 134 | elseif is_admin(msg) then 135 | requester = "admin" 136 | elseif is_momod(msg) then 137 | requester = "moderator" 138 | else 139 | requester = "user" 140 | end 141 | if matches[1] == "!help" then 142 | return telegram_help() 143 | elseif matches[1] == "!help all" then 144 | return help_all(requester) 145 | else 146 | local text = "" 147 | if tonumber(matches[1]) then 148 | text = plugin_help(nil, matches[1], requester) 149 | else 150 | text = plugin_help(matches[1], nil, requester) 151 | end 152 | if not text then 153 | text = telegram_help() 154 | end 155 | return text 156 | end 157 | end 158 | 159 | return { 160 | description = "Help plugin. Get info from other plugins. ", 161 | usage = { 162 | "!help: Show list of plugins.", 163 | "!help all: Show all commands for every plugin.", 164 | "!help [plugin name]: Commands for that plugin.", 165 | "!help [number]: Commands for that plugin. Type !help to get the plugin number." 166 | }, 167 | patterns = { 168 | "^!help$", 169 | "^!help all", 170 | "^!help (.+)" 171 | }, 172 | run = run 173 | } 174 | 175 | end 176 | -------------------------------------------------------------------------------- /plugins/id.lua: -------------------------------------------------------------------------------- 1 | local function user_print_name(user) 2 | if user.print_name then 3 | return user.print_name 4 | end 5 | local text = '' 6 | if user.first_name then 7 | text = user.last_name..' ' 8 | end 9 | if user.lastname then 10 | text = text..user.last_name 11 | end 12 | return text 13 | end 14 | 15 | local function returnids(cb_extra, success, result) 16 | local receiver = cb_extra.receiver 17 | --local chat_id = "chat#id"..result.id 18 | local chat_id = result.id 19 | local chatname = result.print_name 20 | 21 | local text = 'IDs for chat '..chatname 22 | ..' ('..chat_id..')\n' 23 | ..'There are '..result.members_num..' members' 24 | ..'\n---------\n' 25 | i = 0 26 | for k,v in pairs(result.members) do 27 | i = i+1 28 | text = text .. i .. ". " .. string.gsub(v.print_name, "_", " ") .. " (" .. v.id .. ")\n" 29 | end 30 | send_large_msg(receiver, text) 31 | end 32 | 33 | local function username_id(cb_extra, success, result) 34 | local receiver = cb_extra.receiver 35 | local qusername = cb_extra.qusername 36 | local text = 'User '..qusername..' not found in this group!' 37 | for k,v in pairs(result.members) do 38 | vusername = v.username 39 | if vusername == qusername then 40 | text = 'ID for username\n'..vusername..' : '..v.id 41 | end 42 | end 43 | send_large_msg(receiver, text) 44 | end 45 | 46 | local function run(msg, matches) 47 | local receiver = get_receiver(msg) 48 | if matches[1] == "!id" then 49 | local text = 'Name : '.. string.gsub(user_print_name(msg.from),'_', ' ') .. '\nID : ' .. msg.from.id 50 | if is_chat_msg(msg) then 51 | text = text .. "\n\nYou are in group " .. string.gsub(user_print_name(msg.to), '_', ' ') .. " (ID: " .. msg.to.id .. ")" 52 | end 53 | return text 54 | elseif matches[1] == "chat" then 55 | -- !ids? (chat) (%d+) 56 | if matches[2] and is_sudo(msg) then 57 | local chat = 'chat#id'..matches[2] 58 | chat_info(chat, returnids, {receiver=receiver}) 59 | else 60 | if not is_chat_msg(msg) then 61 | return "You are not in a group." 62 | end 63 | local chat = get_receiver(msg) 64 | chat_info(chat, returnids, {receiver=receiver}) 65 | end 66 | else 67 | if not is_chat_msg(msg) then 68 | return "Only works in group" 69 | end 70 | local qusername = string.gsub(matches[1], "@", "") 71 | local chat = get_receiver(msg) 72 | chat_info(chat, username_id, {receiver=receiver, qusername=qusername}) 73 | end 74 | end 75 | 76 | return { 77 | description = "Know your id or the id of a chat members.", 78 | usage = { 79 | "!id: Return your ID and the chat id if you are in one.", 80 | "!ids chat: Return the IDs of the current chat members.", 81 | "!ids chat : Return the IDs of the members.", 82 | "!id : Return the id from username given." 83 | }, 84 | patterns = { 85 | "^!id$", 86 | "^!ids? (chat) (%d+)$", 87 | "^!ids? (chat)$", 88 | "^!id (.*)$" 89 | }, 90 | run = run 91 | } 92 | -------------------------------------------------------------------------------- /plugins/images.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | function run(msg, matches) 4 | local url = matches[1] 5 | local receiver = get_receiver(msg) 6 | send_photo_from_url(receiver, url) 7 | end 8 | 9 | return { 10 | description = "When user sends image URL (ends with png, jpg, jpeg) download and send it to origin.", 11 | usage = "When user sends image URL (ends with png, jpg, jpeg), bot will download and return it. ", 12 | patterns = { 13 | "(https?://[%w-_%.%?%.:/%+=&]+%.png)$", 14 | "(https?://[%w-_%.%?%.:/%+=&]+%.jpg)$", 15 | "(https?://[%w-_%.%?%.:/%+=&]+%.jpeg)$", 16 | }, 17 | run = run 18 | } 19 | 20 | end 21 | -------------------------------------------------------------------------------- /plugins/imdb.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local function imdb(movie) 4 | local http = require("socket.http") 5 | local movie = movie:gsub(' ', '+') 6 | local url = "http://www.omdbapi.com/?t=" .. movie 7 | local response, code, headers = http.request(url) 8 | 9 | if code ~= 200 then 10 | return "Error: " .. code 11 | end 12 | 13 | if #response > 0 then 14 | local r = json:decode(response) 15 | r['Url'] = "http://imdb.com/title/" .. r.imdbID 16 | local t = "" 17 | for k, v in pairs(r) do t = t .. k .. ": " .. v .. ", " end 18 | return t:sub(1, -3) 19 | end 20 | return nil 21 | end 22 | 23 | local function run(msg, matches) 24 | return imdb(matches[1]) 25 | end 26 | 27 | return { 28 | description = "IMDB plugin for telegram", 29 | usage = "!imdb [movie]", 30 | patterns = {"^!imdb (.+)"}, 31 | run = run 32 | } 33 | 34 | end -------------------------------------------------------------------------------- /plugins/img_google.lua: -------------------------------------------------------------------------------- 1 | do 2 | local mime = require("mime") 3 | 4 | local google_config = load_from_file('data/google.lua') 5 | local cache = {} 6 | 7 | --[[ 8 | local function send_request(url) 9 | local t = {} 10 | local options = { 11 | url = url, 12 | sink = ltn12.sink.table(t), 13 | method = "GET" 14 | } 15 | local a, code, headers, status = http.request(options) 16 | return table.concat(t), code, headers, status 17 | end]]-- 18 | 19 | local function get_google_data(text) 20 | local url = "http://ajax.googleapis.com/ajax/services/search/images?" 21 | url = url.."v=1.0&rsz=5" 22 | url = url.."&q="..URL.escape(text) 23 | url = url.."&imgsz=small|medium|large" 24 | if google_config.api_keys then 25 | local i = math.random(#google_config.api_keys) 26 | local api_key = google_config.api_keys[i] 27 | if api_key then 28 | url = url.."&key="..api_key 29 | end 30 | end 31 | 32 | local res, code = http.request(url) 33 | 34 | if code ~= 200 then 35 | print("HTTP Error code:", code) 36 | return nil 37 | end 38 | 39 | local google = json:decode(res) 40 | return google 41 | end 42 | 43 | -- Returns only the useful google data to save on cache 44 | local function simple_google_table(google) 45 | local new_table = {} 46 | new_table.responseData = {} 47 | new_table.responseDetails = google.responseDetails 48 | new_table.responseStatus = google.responseStatus 49 | new_table.responseData.results = {} 50 | local results = google.responseData.results 51 | for k,result in pairs(results) do 52 | new_table.responseData.results[k] = {} 53 | new_table.responseData.results[k].unescapedUrl = result.unescapedUrl 54 | new_table.responseData.results[k].url = result.url 55 | end 56 | return new_table 57 | end 58 | 59 | local function save_to_cache(query, data) 60 | -- Saves result on cache 61 | if string.len(query) <= 7 then 62 | local text_b64 = mime.b64(query) 63 | if not cache[text_b64] then 64 | local simple_google = simple_google_table(data) 65 | cache[text_b64] = simple_google 66 | end 67 | end 68 | end 69 | 70 | local function process_google_data(google, receiver, query) 71 | if google.responseStatus == 403 then 72 | local text = 'ERROR: Reached maximum searches per day' 73 | send_msg(receiver, text, ok_cb, false) 74 | 75 | elseif google.responseStatus == 200 then 76 | local data = google.responseData 77 | 78 | if not data or not data.results or #data.results == 0 then 79 | local text = 'Image not found.' 80 | send_msg(receiver, text, ok_cb, false) 81 | return false 82 | end 83 | 84 | -- Random image from table 85 | local i = math.random(#data.results) 86 | local url = data.results[i].unescapedUrl or data.results[i].url 87 | local old_timeout = http.TIMEOUT or 10 88 | http.TIMEOUT = 5 89 | send_photo_from_url(receiver, url) 90 | http.TIMEOUT = old_timeout 91 | 92 | save_to_cache(query, google) 93 | 94 | else 95 | local text = 'ERROR!' 96 | send_msg(receiver, text, ok_cb, false) 97 | end 98 | end 99 | 100 | function run(msg, matches) 101 | local receiver = get_receiver(msg) 102 | local text = matches[1] 103 | local text_b64 = mime.b64(text) 104 | local cached = cache[text_b64] 105 | if cached then 106 | process_google_data(cached, receiver, text) 107 | else 108 | local data = get_google_data(text) 109 | process_google_data(data, receiver, text) 110 | end 111 | end 112 | 113 | return { 114 | description = "Search image with Google API and sends it.", 115 | usage = "!img [term]: Random search an image with Google API.", 116 | patterns = { 117 | "^!img (.*)$" 118 | }, 119 | run = run 120 | } 121 | 122 | end 123 | -------------------------------------------------------------------------------- /plugins/invite.lua: -------------------------------------------------------------------------------- 1 | -- Invite other user to the chat group. 2 | -- Use !invite name User_name or !invite id id_number 3 | -- The User_name is the print_name (there are no spaces but _) 4 | 5 | do 6 | 7 | local function callback(extra, success, result) 8 | vardump(success) 9 | vardump(result) 10 | end 11 | 12 | local function run(msg, matches) 13 | local user = matches[2] 14 | 15 | -- User submitted a user name 16 | if matches[1] == "name" then 17 | user = string.gsub(user," ","_") 18 | end 19 | 20 | -- User submitted an id 21 | if matches[1] == "id" then 22 | user = 'user#id'..user 23 | end 24 | 25 | -- The message must come from a chat group 26 | if msg.to.type == 'chat' then 27 | local chat = 'chat#id'..msg.to.id 28 | chat_add_user(chat, user, callback, false) 29 | return "Add: "..user.." to "..chat 30 | else 31 | return 'This isnt a chat group!' 32 | end 33 | 34 | end 35 | 36 | return { 37 | description = "Invite other user to the chat group", 38 | usage = { 39 | "!invite name [user_name]", 40 | "!invite id [user_id]" }, 41 | patterns = { 42 | "^!invite (name) (.*)$", 43 | "^!invite (id) (%d+)$" 44 | }, 45 | run = run, 46 | moderation = true 47 | } 48 | 49 | end -------------------------------------------------------------------------------- /plugins/isX.lua: -------------------------------------------------------------------------------- 1 | local https = require "ssl.https" 2 | local ltn12 = require "ltn12" 3 | 4 | local function request(imageUrl) 5 | -- Edit data/mashape.lua with your Mashape API key 6 | -- http://docs.mashape.com/api-keys 7 | local mashape = load_from_file('data/mashape.lua', { 8 | api_key = '' 9 | }) 10 | 11 | local api_key = mashape.api_key 12 | if api_key:isempty() then 13 | return nil, 'Configure your Mashape API Key' 14 | end 15 | 16 | local api = "https://sphirelabs-advanced-porn-nudity-and-adult-content-detection.p.mashape.com/v1/get/index.php?" 17 | local parameters = "&url="..(URL.escape(imageUrl) or "") 18 | local url = api..parameters 19 | local respbody = {} 20 | local headers = { 21 | ["X-Mashape-Key"] = api_key, 22 | ["Accept"] = "Accept: application/json" 23 | } 24 | print(url) 25 | local body, code, headers, status = https.request{ 26 | url = url, 27 | method = "GET", 28 | headers = headers, 29 | sink = ltn12.sink.table(respbody), 30 | protocol = "tlsv1" 31 | } 32 | if code ~= 200 then return "", code end 33 | local body = table.concat(respbody) 34 | return body, code 35 | end 36 | 37 | local function parseData(data) 38 | local jsonBody = json:decode(data) 39 | local response = "" 40 | print(data) 41 | if jsonBody["Error Occured"] ~= nil then 42 | response = response .. jsonBody["Error Occured"] 43 | elseif jsonBody["Is Porn"] == nil or jsonBody["Reason"] == nil then 44 | response = response .. "I don't know if that has adult content or not." 45 | else 46 | if jsonBody["Is Porn"] == "True" then 47 | response = response .. "Beware!\n" 48 | end 49 | response = response .. jsonBody["Reason"] 50 | end 51 | return jsonBody["Is Porn"], response 52 | end 53 | 54 | local function run(msg, matches) 55 | local data, code = request(matches[1]) 56 | if code ~= 200 then return "There was an error. "..code end 57 | local isPorn, result = parseData(data) 58 | return result 59 | end 60 | 61 | return { 62 | description = "Does this photo contain adult content?", 63 | usage = { 64 | "!isx [url]", 65 | "!isporn [url]" 66 | }, 67 | patterns = { 68 | "^!is[x|X] (.*)$", 69 | "^!is[p|P]orn (.*)$" 70 | }, 71 | run = run 72 | } -------------------------------------------------------------------------------- /plugins/isup.lua: -------------------------------------------------------------------------------- 1 | do 2 | local socket = require("socket") 3 | local cronned = load_from_file('data/isup.lua') 4 | 5 | local function save_cron(msg, url, delete) 6 | local origin = get_receiver(msg) 7 | if not cronned[origin] then 8 | cronned[origin] = {} 9 | end 10 | if not delete then 11 | table.insert(cronned[origin], url) 12 | else 13 | for k,v in pairs(cronned[origin]) do 14 | if v == url then 15 | table.remove(cronned[origin], k) 16 | end 17 | end 18 | end 19 | serialize_to_file(cronned, 'data/isup.lua') 20 | return 'Saved!' 21 | end 22 | 23 | local function is_up_socket(ip, port) 24 | print('Connect to', ip, port) 25 | local c = socket.try(socket.tcp()) 26 | c:settimeout(3) 27 | local conn = c:connect(ip, port) 28 | if not conn then 29 | return false 30 | else 31 | c:close() 32 | return true 33 | end 34 | end 35 | 36 | local function is_up_http(url) 37 | -- Parse URL from input, default to http 38 | local parsed_url = URL.parse(url, { scheme = 'http', authority = '' }) 39 | -- Fix URLs without subdomain not parsed properly 40 | if not parsed_url.host and parsed_url.path then 41 | parsed_url.host = parsed_url.path 42 | parsed_url.path = "" 43 | end 44 | -- Re-build URL 45 | local url = URL.build(parsed_url) 46 | 47 | local protocols = { 48 | ["https"] = https, 49 | ["http"] = http 50 | } 51 | local options = { 52 | url = url, 53 | redirect = false, 54 | method = "GET" 55 | } 56 | local response = { protocols[parsed_url.scheme].request(options) } 57 | local code = tonumber(response[2]) 58 | if code == nil or code >= 400 then 59 | return false 60 | end 61 | return true 62 | end 63 | 64 | local function isup(url) 65 | local pattern = '^(%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?):?(%d?%d?%d?%d?%d?)$' 66 | local ip,port = string.match(url, pattern) 67 | local result = nil 68 | 69 | -- !isup 8.8.8.8:53 70 | if ip then 71 | port = port or '80' 72 | result = is_up_socket(ip, port) 73 | else 74 | result = is_up_http(url) 75 | end 76 | 77 | return result 78 | end 79 | 80 | local function cron() 81 | for chan, urls in pairs(cronned) do 82 | for k,url in pairs(urls) do 83 | print('Checking', url) 84 | if not isup(url) then 85 | local text = url..' looks DOWN from here. 😱' 86 | send_msg(chan, text, ok_cb, false) 87 | end 88 | end 89 | end 90 | end 91 | 92 | local function run(msg, matches) 93 | if matches[1] == 'cron delete' then 94 | if not is_sudo(msg) then 95 | return 'This command requires privileged user' 96 | end 97 | return save_cron(msg, matches[2], true) 98 | 99 | elseif matches[1] == 'cron' then 100 | if not is_sudo(msg) then 101 | return 'This command requires privileged user' 102 | end 103 | return save_cron(msg, matches[2]) 104 | 105 | elseif isup(matches[1]) then 106 | return matches[1]..' looks UP from here. 😃' 107 | else 108 | return matches[1]..' looks DOWN from here. 😱' 109 | end 110 | end 111 | 112 | return { 113 | description = "Check if a website or server is up.", 114 | usage = { 115 | "!isup [host]: Performs a HTTP request or Socket (ip:port) connection", 116 | "!isup cron [host]: Every 5mins check if host is up. (Requires privileged user)", 117 | "!isup cron delete [host]: Disable checking that host." 118 | }, 119 | patterns = { 120 | "^!isup (cron delete) (.*)$", 121 | "^!isup (cron) (.*)$", 122 | "^!isup (.*)$", 123 | "^!ping (.*)$", 124 | "^!ping (cron delete) (.*)$", 125 | "^!ping (cron) (.*)$" 126 | }, 127 | run = run, 128 | cron = cron 129 | } 130 | 131 | end 132 | -------------------------------------------------------------------------------- /plugins/join.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local function parsed_url(link) 4 | local parsed_link = URL.parse(link) 5 | local parsed_path = URL.parse_path(parsed_link.path) 6 | return parsed_path[2] 7 | end 8 | 9 | function run(msg, matches) 10 | local hash = parsed_url(matches[1]) 11 | join = import_chat_link(hash,ok_cb,false) 12 | end 13 | 14 | 15 | return { 16 | description = "Invite bot into a group chat", 17 | usage = "!join [invite link]", 18 | patterns = { 19 | "^!join (.*)$" 20 | }, 21 | run = run 22 | } 23 | 24 | end 25 | -------------------------------------------------------------------------------- /plugins/location.lua: -------------------------------------------------------------------------------- 1 | -- Implement a command !loc [area] which uses 2 | -- the static map API to get a location image 3 | 4 | -- Not sure if this is the proper way 5 | -- Intent: get_latlong is in time.lua, we need it here 6 | -- loadfile "time.lua" 7 | 8 | -- Globals 9 | -- If you have a google api key for the geocoding/timezone api 10 | do 11 | 12 | local api_key = nil 13 | 14 | local base_api = "https://maps.googleapis.com/maps/api" 15 | 16 | function get_staticmap(area) 17 | local api = base_api .. "/staticmap?" 18 | 19 | -- Get a sense of scale 20 | local lat,lng,acc,types = get_latlong(area) 21 | 22 | local scale = types[1] 23 | if scale=="locality" then zoom=8 24 | elseif scale=="country" then zoom=4 25 | else zoom = 13 end 26 | 27 | local parameters = 28 | "size=600x300" .. 29 | "&zoom=" .. zoom .. 30 | "¢er=" .. URL.escape(area) .. 31 | "&markers=color:red"..URL.escape("|"..area) 32 | 33 | if api_key ~=nil and api_key ~= "" then 34 | parameters = parameters .. "&key="..api_key 35 | end 36 | return lat, lng, api..parameters 37 | end 38 | 39 | 40 | function run(msg, matches) 41 | local receiver = get_receiver(msg) 42 | local lat,lng,url = get_staticmap(matches[1]) 43 | 44 | -- Send the actual location, is a google maps link 45 | send_location(receiver, lat, lng, ok_cb, false) 46 | 47 | -- Send a picture of the map, which takes scale into account 48 | send_photo_from_url(receiver, url) 49 | 50 | -- Return a link to the google maps stuff is now not needed anymore 51 | return nil 52 | end 53 | 54 | return { 55 | description = "Gets information about a location, maplink and overview", 56 | usage = "!loc (location): Gets information about a location, maplink and overview", 57 | patterns = {"^!loc (.*)$"}, 58 | run = run 59 | } 60 | 61 | end -------------------------------------------------------------------------------- /plugins/lyrics.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local BASE_LNM_URL = 'http://api.lyricsnmusic.com/songs' 4 | local LNM_APIKEY = '1f5ea5cf652d9b2ba5a5118a11dba5' 5 | 6 | local BASE_LYRICS_URL = 'http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect' 7 | 8 | local function getInfo(query) 9 | print('Getting info of ' .. query) 10 | 11 | local url = BASE_LNM_URL..'?api_key='..LNM_APIKEY 12 | ..'&q='..URL.escape(query) 13 | 14 | local b, c = http.request(url) 15 | if c ~= 200 then 16 | return nil 17 | end 18 | 19 | local result = json:decode(b) 20 | local artist = result[1].artist.name 21 | local track = result[1].title 22 | return artist, track 23 | end 24 | 25 | local function getLyrics(query) 26 | 27 | local artist, track = getInfo(query) 28 | if artist and track then 29 | local url = BASE_LYRICS_URL..'?artist='..URL.escape(artist) 30 | ..'&song='..URL.escape(track) 31 | 32 | local b, c = http.request(url) 33 | if c ~= 200 then 34 | return nil 35 | end 36 | 37 | local xml = require("xml") 38 | local result = xml.load(b) 39 | 40 | if not result then 41 | return nil 42 | end 43 | 44 | if xml.find(result, 'LyricSong') then 45 | track = xml.find(result, 'LyricSong')[1] 46 | end 47 | 48 | if xml.find(result, 'LyricArtist') then 49 | artist = xml.find(result, 'LyricArtist')[1] 50 | end 51 | 52 | local lyric 53 | if xml.find(result, 'Lyric') then 54 | lyric = xml.find(result, 'Lyric')[1] 55 | else 56 | lyric = nil 57 | end 58 | 59 | local cover 60 | if xml.find(result, 'LyricCovertArtUrl') then 61 | cover = xml.find(result, 'LyricCovertArtUrl')[1] 62 | else 63 | cover = nil 64 | end 65 | 66 | return artist, track, lyric, cover 67 | 68 | else 69 | return nil 70 | end 71 | 72 | end 73 | 74 | 75 | local function run(msg, matches) 76 | local artist, track, lyric, cover = getLyrics(matches[1]) 77 | if track and artist and lyric then 78 | if cover then 79 | local receiver = get_receiver(msg) 80 | send_photo_from_url(receiver, cover) 81 | end 82 | return '🎵 ' .. artist .. ' - ' .. track .. ' 🎵\n----------\n' .. lyric 83 | else 84 | return 'Oops! Lyrics not found or something like that! :/' 85 | end 86 | end 87 | 88 | return { 89 | description = 'Getting lyrics of a song', 90 | usage = '!lyrics [track or artist - track]: Search and get lyrics of the song', 91 | patterns = { 92 | '^!lyrics? (.*)$' 93 | }, 94 | run = run 95 | } 96 | 97 | end 98 | -------------------------------------------------------------------------------- /plugins/magic8ball.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | function run(msg, matches) 4 | local answers = {'It is certain','It is decidedly so','Without a doubt', 5 | 'Yes definitely','You may rely on it','As I see it, yes', 6 | 'Most likely','Outlook good','Yes','Signs point to yes', 7 | 'Reply hazy try again','Ask again later', 8 | 'Better not tell you now','Cannot predict now', 9 | 'Concentrate and ask again','Don\'t count on it', 10 | 'My reply is no','My sources say no','Outlook not so good', 11 | 'Very doubtful'} 12 | return answers[math.random(#answers)] 13 | end 14 | 15 | return { 16 | description = "Magic 8Ball", 17 | usage = "!magic8ball", 18 | patterns = {"^!magic8ball"}, 19 | run = run 20 | } 21 | 22 | end 23 | -------------------------------------------------------------------------------- /plugins/media.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local function run(msg, matches) 4 | local receiver = get_receiver(msg) 5 | local url = matches[1] 6 | local ext = matches[2] 7 | 8 | local file = download_to_file(url) 9 | local cb_extra = {file_path=file} 10 | 11 | local mime_type = mimetype.get_content_type_no_sub(ext) 12 | 13 | if ext == 'gif' then 14 | print('send_file') 15 | send_document(receiver, file, rmtmp_cb, cb_extra) 16 | 17 | elseif mime_type == 'text' then 18 | print('send_document') 19 | send_document(receiver, file, rmtmp_cb, cb_extra) 20 | 21 | elseif mime_type == 'image' then 22 | print('send_photo') 23 | send_photo(receiver, file, rmtmp_cb, cb_extra) 24 | 25 | elseif mime_type == 'audio' then 26 | print('send_audio') 27 | send_audio(receiver, file, rmtmp_cb, cb_extra) 28 | 29 | elseif mime_type == 'video' then 30 | print('send_video') 31 | send_video(receiver, file, rmtmp_cb, cb_extra) 32 | 33 | else 34 | print('send_file') 35 | send_file(receiver, file, rmtmp_cb, cb_extra) 36 | end 37 | 38 | end 39 | 40 | return { 41 | description = "When user sends media URL (ends with gif, mp4, pdf, etc.) download and send it to origin.", 42 | usage = "When user sends media URL (ends with gif, mp4, pdf, etc.) download and send it to origin.", 43 | patterns = { 44 | "(https?://[%w-_%.%?%.:/%+=&]+%.(gif))$", 45 | "(https?://[%w-_%.%?%.:/%+=&]+%.(mp4))$", 46 | "(https?://[%w-_%.%?%.:/%+=&]+%.(pdf))$", 47 | "(https?://[%w-_%.%?%.:/%+=&]+%.(ogg))$", 48 | "(https?://[%w-_%.%?%.:/%+=&]+%.(zip))$", 49 | "(https?://[%w-_%.%?%.:/%+=&]+%.(mp3))$", 50 | "(https?://[%w-_%.%?%.:/%+=&]+%.(rar))$", 51 | "(https?://[%w-_%.%?%.:/%+=&]+%.(wmv))$", 52 | "(https?://[%w-_%.%?%.:/%+=&]+%.(doc))$", 53 | "(https?://[%w-_%.%?%.:/%+=&]+%.(avi))$", 54 | "(https?://[%w-_%.%?%.:/%+=&]+%.(webp))$" 55 | }, 56 | run = run 57 | } 58 | 59 | end 60 | -------------------------------------------------------------------------------- /plugins/media_handler.lua: -------------------------------------------------------------------------------- 1 | local function run(msg, matches) 2 | if msg.media then 3 | if msg.media.type == 'document' then 4 | print('Document file') 5 | end 6 | if msg.media.type == 'photo' then 7 | print('Photo file') 8 | end 9 | if msg.media.type == 'video' then 10 | print('Video file') 11 | end 12 | if msg.media.type == 'audio' then 13 | print('Audio file') 14 | end 15 | end 16 | end 17 | 18 | local function pre_process(msg) 19 | if not msg.text and msg.media then 20 | msg.text = '['..msg.media.type..']' 21 | end 22 | return msg 23 | end 24 | 25 | return { 26 | description = "Media handler.", 27 | usage = "Bot media handler, no usage.", 28 | run = run, 29 | patterns = { 30 | '%[(document)%]', 31 | '%[(photo)%]', 32 | '%[(video)%]', 33 | '%[(audio)%]' 34 | }, 35 | hide = true, 36 | pre_process = pre_process 37 | } 38 | -------------------------------------------------------------------------------- /plugins/meme.lua: -------------------------------------------------------------------------------- 1 | local helpers = require "OAuth.helpers" 2 | 3 | local _file_memes = './data/memes.lua' 4 | local _cache = {} 5 | 6 | local function post_petition(url, arguments) 7 | local response_body = {} 8 | local request_constructor = { 9 | url = url, 10 | method = "POST", 11 | sink = ltn12.sink.table(response_body), 12 | headers = {}, 13 | redirect = false 14 | } 15 | 16 | local source = arguments 17 | if type(arguments) == "table" then 18 | local source = helpers.url_encode_arguments(arguments) 19 | end 20 | request_constructor.headers["Content-Type"] = "application/x-www-form-urlencoded" 21 | request_constructor.headers["Content-Length"] = tostring(#source) 22 | request_constructor.source = ltn12.source.string(source) 23 | 24 | local ok, response_code, response_headers, response_status_line = http.request(request_constructor) 25 | 26 | if not ok then 27 | return nil 28 | end 29 | 30 | response_body = json:decode(table.concat(response_body)) 31 | 32 | return response_body 33 | end 34 | 35 | local function upload_memes(memes) 36 | local base = "http://hastebin.com/" 37 | local pet = post_petition(base .. "documents", memes) 38 | if pet == nil then 39 | return '', '' 40 | end 41 | local key = pet.key 42 | return base .. key, base .. 'raw/' .. key 43 | end 44 | 45 | local function analyze_meme_list() 46 | local function get_m(res, n) 47 | local r = "(.*).*" 48 | local start = string.find(res, "", n) 49 | if start == nil then 50 | return nil, nil 51 | end 52 | local final = string.find(res, "", n) + #"" 53 | local sub = string.sub(res, start, final) 54 | local f = string.match(sub, r) 55 | return f, final 56 | end 57 | local res, code = http.request('http://apimeme.com/') 58 | local r = "(.*).*" 59 | local n = 0 60 | local f, n = get_m(res, n) 61 | local ult = {} 62 | while f ~= nil do 63 | print(f) 64 | table.insert(ult, f) 65 | f, n = get_m(res, n) 66 | end 67 | return ult 68 | end 69 | 70 | 71 | local function get_memes() 72 | local memes = analyze_meme_list() 73 | return { 74 | last_time = os.time(), 75 | memes = memes 76 | } 77 | end 78 | 79 | local function load_data() 80 | local data = load_from_file(_file_memes) 81 | if not next(data) or data.memes == {} or os.time() - data.last_time > 86400 then 82 | data = get_memes() 83 | -- Upload only if changed? 84 | link, rawlink = upload_memes(table.concat(data.memes, '\n')) 85 | data.link = link 86 | data.rawlink = rawlink 87 | serialize_to_file(data, _file_memes) 88 | end 89 | return data 90 | end 91 | 92 | local function match_n_word(list1, list2) 93 | local n = 0 94 | for k,v in pairs(list1) do 95 | for k2, v2 in pairs(list2) do 96 | if v2:find(v) then 97 | n = n + 1 98 | end 99 | end 100 | end 101 | return n 102 | end 103 | 104 | local function match_meme(name) 105 | local _memes = load_data() 106 | local name = name:lower():split(' ') 107 | local max = 0 108 | local id = nil 109 | for k,v in pairs(_memes.memes) do 110 | local n = match_n_word(name, v:lower():split(' ')) 111 | if n > 0 and n > max then 112 | max = n 113 | id = v 114 | end 115 | end 116 | return id 117 | end 118 | 119 | local function generate_meme(id, textup, textdown) 120 | local base = "http://apimeme.com/meme" 121 | local arguments = { 122 | meme=id, 123 | top=textup, 124 | bottom=textdown 125 | } 126 | return base .. "?" .. helpers.url_encode_arguments(arguments) 127 | end 128 | 129 | local function get_all_memes_names() 130 | local _memes = load_data() 131 | local text = 'Last time: ' .. _memes.last_time .. '\n-----------\n' 132 | for k, v in pairs(_memes.memes) do 133 | text = text .. '- ' .. v .. '\n' 134 | end 135 | text = text .. '--------------\n' .. 'You can see the images here: http://apimeme.com/' 136 | return text 137 | end 138 | 139 | local function callback_send(cb_extra, success, data) 140 | if success == 0 then 141 | send_msg(cb_extra.receiver, "Something wrong happened, probably that meme had been removed from server: " .. cb_extra.url, ok_cb, false) 142 | end 143 | end 144 | 145 | local function run(msg, matches) 146 | local receiver = get_receiver(msg) 147 | if matches[1] == 'list' then 148 | local _memes = load_data() 149 | return 'I have ' .. #_memes.memes .. ' meme names.\nCheck this link to see all :)\n' .. _memes.link 150 | elseif matches[1] == 'listall' then 151 | if not is_sudo(msg) then 152 | return "You can't list this way, use \"!meme list\"" 153 | else 154 | return get_all_memes_names() 155 | end 156 | elseif matches[1] == "search" then 157 | local meme_id = match_meme(matches[2]) 158 | if meme_id == nil then 159 | return "I can't match that search with any meme." 160 | end 161 | return "With that search your meme is " .. meme_id 162 | end 163 | local searchterm = string.gsub(matches[1]:lower(), ' ', '') 164 | 165 | local meme_id = _cache[searchterm] or match_meme(matches[1]) 166 | if not meme_id then 167 | return 'I don\'t understand the meme name "' .. matches[1] .. '"' 168 | end 169 | 170 | _cache[searchterm] = meme_id 171 | print("Generating meme: " .. meme_id .. " with texts " .. matches[2] .. ' and ' .. matches[3]) 172 | local url_gen = generate_meme(meme_id, matches[2], matches[3]) 173 | send_photo_from_url(receiver, url_gen, callback_send, {receiver=receiver, url=url_gen}) 174 | 175 | return nil 176 | end 177 | 178 | return { 179 | description = "Generate a meme image with up and bottom texts.", 180 | usage = { 181 | "!meme search (name): Return the name of the meme that match.", 182 | "!meme list: Return the link where you can see the memes.", 183 | "!meme listall: Return the list of all memes. Only admin can call it.", 184 | '!meme [name] - [text_up] - [text_down]: Generate a meme with the picture that match with that name with the texts provided.', 185 | '!meme [name] "[text_up]" "[text_down]": Generate a meme with the picture that match with that name with the texts provided.', 186 | }, 187 | patterns = { 188 | "^!meme (search) (.+)$", 189 | '^!meme (list)$', 190 | '^!meme (listall)$', 191 | '^!meme (.+) "(.*)" "(.*)"$', 192 | '^!meme "(.+)" "(.*)" "(.*)"$', 193 | "^!meme (.+) %- (.*) %- (.*)$" 194 | }, 195 | run = run 196 | } 197 | -------------------------------------------------------------------------------- /plugins/minecraft.lua: -------------------------------------------------------------------------------- 1 | local usage = { 2 | "!mine [ip]: Searches Minecraft server on specified ip and sends info. Default port: 25565", 3 | "!mine [ip] [port]: Searches Minecraft server on specified ip and port and sends info.", 4 | } 5 | local ltn12 = require "ltn12" 6 | 7 | local function mineSearch(ip, port, receiver) --25565 8 | local responseText = "" 9 | local api = "https://api.syfaro.net/server/status" 10 | local parameters = "?ip="..(URL.escape(ip) or "").."&port="..(URL.escape(port) or "").."&players=true&favicon=true" 11 | local http = require("socket.http") 12 | local respbody = {} 13 | local body, code, headers, status = http.request{ 14 | url = api..parameters, 15 | method = "GET", 16 | redirect = true, 17 | sink = ltn12.sink.table(respbody) 18 | } 19 | local body = table.concat(respbody) 20 | if (status == nil) then return "ERROR: status = nil" end 21 | if code ~=200 then return "ERROR: "..code..". Status: "..status end 22 | local jsonData = json:decode(body) 23 | responseText = responseText..ip..":"..port.." ->\n" 24 | if (jsonData.motd ~= nil) then 25 | local tempMotd = "" 26 | tempMotd = jsonData.motd:gsub('%§.', '') 27 | if (jsonData.motd ~= nil) then responseText = responseText.." Motd: "..tempMotd.."\n" end 28 | end 29 | if (jsonData.online ~= nil) then 30 | responseText = responseText.." Online: "..tostring(jsonData.online).."\n" 31 | end 32 | if (jsonData.players ~= nil) then 33 | if (jsonData.players.max ~= nil) then 34 | responseText = responseText.." Max Players: "..jsonData.players.max.."\n" 35 | end 36 | if (jsonData.players.now ~= nil) then 37 | responseText = responseText.." Players online: "..jsonData.players.now.."\n" 38 | end 39 | if (jsonData.players.sample ~= nil and jsonData.players.sample ~= false) then 40 | responseText = responseText.." Players: "..table.concat(jsonData.players.sample, ", ").."\n" 41 | end 42 | end 43 | if (jsonData.favicon ~= nil and false) then 44 | --send_photo(receiver, jsonData.favicon) --(decode base64 and send) 45 | end 46 | return responseText 47 | end 48 | 49 | local function parseText(chat, text) 50 | if (text == nil or text == "!mine") then 51 | return usage 52 | end 53 | ip, port = string.match(text, "^!mine (.-) (.*)$") 54 | if (ip ~= nil and port ~= nil) then 55 | return mineSearch(ip, port, chat) 56 | end 57 | local ip = string.match(text, "^!mine (.*)$") 58 | if (ip ~= nil) then 59 | return mineSearch(ip, "25565", chat) 60 | end 61 | return "ERROR: no input ip?" 62 | end 63 | 64 | 65 | local function run(msg, matches) 66 | local chat_id = tostring(msg.to.id) 67 | local result = parseText(chat_id, msg.text) 68 | return result 69 | end 70 | 71 | return { 72 | description = "Searches Minecraft server and sends info", 73 | usage = usage, 74 | patterns = { 75 | "^!mine (.*)$" 76 | }, 77 | run = run 78 | } -------------------------------------------------------------------------------- /plugins/moderation.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local function check_member(cb_extra, success, result) 4 | local receiver = cb_extra.receiver 5 | local data = cb_extra.data 6 | local msg = cb_extra.msg 7 | for k,v in pairs(result.members) do 8 | local member_id = v.id 9 | if member_id ~= our_id then 10 | local username = v.username 11 | data[tostring(msg.to.id)] = { 12 | moderators = {[tostring(member_id)] = username}, 13 | settings = { 14 | set_name = string.gsub(msg.to.print_name, '_', ' '), 15 | lock_name = 'no', 16 | lock_photo = 'no', 17 | lock_member = 'no' 18 | } 19 | } 20 | save_data(_config.moderation.data, data) 21 | return send_large_msg(receiver, 'You have been promoted as moderator for this group.') 22 | end 23 | end 24 | end 25 | 26 | local function automodadd(msg) 27 | local data = load_data(_config.moderation.data) 28 | if msg.action.type == 'chat_created' then 29 | receiver = get_receiver(msg) 30 | chat_info(receiver, check_member,{receiver=receiver, data=data, msg = msg}) 31 | else 32 | if data[tostring(msg.to.id)] then 33 | return 'Group is already added.' 34 | end 35 | if msg.from.username then 36 | username = msg.from.username 37 | else 38 | username = msg.from.print_name 39 | end 40 | -- create data array in moderation.json 41 | data[tostring(msg.to.id)] = { 42 | moderators ={[tostring(msg.from.id)] = username}, 43 | settings = { 44 | set_name = string.gsub(msg.to.print_name, '_', ' '), 45 | lock_name = 'no', 46 | lock_photo = 'no', 47 | lock_member = 'no' 48 | } 49 | } 50 | save_data(_config.moderation.data, data) 51 | return 'Group has been added, and @'..username..' has been promoted as moderator for this group.' 52 | end 53 | end 54 | 55 | local function modadd(msg) 56 | -- superuser and admins only (because sudo are always has privilege) 57 | if not is_admin(msg) then 58 | return "You're not admin" 59 | end 60 | local data = load_data(_config.moderation.data) 61 | if data[tostring(msg.to.id)] then 62 | return 'Group is already added.' 63 | end 64 | -- create data array in moderation.json 65 | data[tostring(msg.to.id)] = { 66 | moderators ={}, 67 | settings = { 68 | set_name = string.gsub(msg.to.print_name, '_', ' '), 69 | lock_name = 'no', 70 | lock_photo = 'no', 71 | lock_member = 'no' 72 | } 73 | } 74 | save_data(_config.moderation.data, data) 75 | 76 | return 'Group has been added.' 77 | end 78 | 79 | local function modrem(msg) 80 | -- superuser and admins only (because sudo are always has privilege) 81 | if not is_admin(msg) then 82 | return "You're not admin" 83 | end 84 | local data = load_data(_config.moderation.data) 85 | local receiver = get_receiver(msg) 86 | if not data[tostring(msg.to.id)] then 87 | return 'Group is not added.' 88 | end 89 | 90 | data[tostring(msg.to.id)] = nil 91 | save_data(_config.moderation.data, data) 92 | 93 | return 'Group has been removed' 94 | end 95 | 96 | local function promote(receiver, member_username, member_id) 97 | local data = load_data(_config.moderation.data) 98 | local group = string.gsub(receiver, 'chat#id', '') 99 | if not data[group] then 100 | return send_large_msg(receiver, 'Group is not added.') 101 | end 102 | if data[group]['moderators'][tostring(member_id)] then 103 | return send_large_msg(receiver, member_username..' is already a moderator.') 104 | end 105 | data[group]['moderators'][tostring(member_id)] = member_username 106 | save_data(_config.moderation.data, data) 107 | return send_large_msg(receiver, '@'..member_username..' has been promoted.') 108 | end 109 | 110 | local function demote(receiver, member_username, member_id) 111 | local data = load_data(_config.moderation.data) 112 | local group = string.gsub(receiver, 'chat#id', '') 113 | if not data[group] then 114 | return send_large_msg(receiver, 'Group is not added.') 115 | end 116 | if not data[group]['moderators'][tostring(member_id)] then 117 | return send_large_msg(receiver, member_username..' is not a moderator.') 118 | end 119 | data[group]['moderators'][tostring(member_id)] = nil 120 | save_data(_config.moderation.data, data) 121 | return send_large_msg(receiver, '@'..member_username..' has been demoted.') 122 | end 123 | 124 | local function admin_promote(receiver, member_username, member_id) 125 | local data = load_data(_config.moderation.data) 126 | if not data['admins'] then 127 | data['admins'] = {} 128 | save_data(_config.moderation.data, data) 129 | end 130 | 131 | if data['admins'][tostring(member_id)] then 132 | return send_large_msg(receiver, member_username..' is already as admin.') 133 | end 134 | 135 | data['admins'][tostring(member_id)] = member_username 136 | save_data(_config.moderation.data, data) 137 | return send_large_msg(receiver, '@'..member_username..' has been promoted as admin.') 138 | end 139 | 140 | local function admin_demote(receiver, member_username, member_id) 141 | local data = load_data(_config.moderation.data) 142 | if not data['admins'] then 143 | data['admins'] = {} 144 | save_data(_config.moderation.data, data) 145 | end 146 | 147 | if not data['admins'][tostring(member_id)] then 148 | return send_large_msg(receiver, member_username..' is not an admin.') 149 | end 150 | 151 | data['admins'][tostring(member_id)] = nil 152 | save_data(_config.moderation.data, data) 153 | 154 | return send_large_msg(receiver, 'Admin '..member_username..' has been demoted.') 155 | end 156 | 157 | local function username_id(cb_extra, success, result) 158 | local mod_cmd = cb_extra.mod_cmd 159 | local receiver = cb_extra.receiver 160 | local member = cb_extra.member 161 | local text = 'No user @'..member..' in this group.' 162 | for k,v in pairs(result.members) do 163 | vusername = v.username 164 | if vusername == member then 165 | member_username = member 166 | member_id = v.id 167 | if mod_cmd == 'promote' then 168 | return promote(receiver, member_username, member_id) 169 | elseif mod_cmd == 'demote' then 170 | return demote(receiver, member_username, member_id) 171 | elseif mod_cmd == 'adminprom' then 172 | return admin_promote(receiver, member_username, member_id) 173 | elseif mod_cmd == 'admindem' then 174 | return admin_demote(receiver, member_username, member_id) 175 | end 176 | end 177 | end 178 | send_large_msg(receiver, text) 179 | end 180 | 181 | local function modlist(msg) 182 | local data = load_data(_config.moderation.data) 183 | if not data[tostring(msg.to.id)] then 184 | return 'Group is not added.' 185 | end 186 | -- determine if table is empty 187 | if next(data[tostring(msg.to.id)]['moderators']) == nil then --fix way 188 | return 'No moderator in this group.' 189 | end 190 | local message = 'List of moderators for ' .. string.gsub(msg.to.print_name, '_', ' ') .. ':\n' 191 | for k,v in pairs(data[tostring(msg.to.id)]['moderators']) do 192 | message = message .. '- '..v..' [' ..k.. '] \n' 193 | end 194 | 195 | return message 196 | end 197 | 198 | local function admin_list(msg) 199 | local data = load_data(_config.moderation.data) 200 | if not data['admins'] then 201 | data['admins'] = {} 202 | save_data(_config.moderation.data, data) 203 | end 204 | if next(data['admins']) == nil then --fix way 205 | return 'No admin available.' 206 | end 207 | local message = 'List for Bot admins:\n' 208 | for k,v in pairs(data['admins']) do 209 | message = message .. '- ' .. v ..' ['..k..'] \n' 210 | end 211 | return message 212 | end 213 | 214 | function run(msg, matches) 215 | if matches[1] == 'debug' then 216 | return debugs(msg) 217 | end 218 | if not is_chat_msg(msg) then 219 | return "Only works on group" 220 | end 221 | local mod_cmd = matches[1] 222 | local receiver = get_receiver(msg) 223 | if matches[1] == 'modadd' then 224 | return modadd(msg) 225 | end 226 | if matches[1] == 'modrem' then 227 | return modrem(msg) 228 | end 229 | if matches[1] == 'promote' and matches[2] then 230 | if not is_momod(msg) then 231 | return "Only moderator can promote" 232 | end 233 | local member = string.gsub(matches[2], "@", "") 234 | chat_info(receiver, username_id, {mod_cmd= mod_cmd, receiver=receiver, member=member}) 235 | end 236 | if matches[1] == 'demote' and matches[2] then 237 | if not is_momod(msg) then 238 | return "Only moderator can demote" 239 | end 240 | if string.gsub(matches[2], "@", "") == msg.from.username then 241 | return "You can't demote yourself" 242 | end 243 | local member = string.gsub(matches[2], "@", "") 244 | chat_info(receiver, username_id, {mod_cmd= mod_cmd, receiver=receiver, member=member}) 245 | end 246 | if matches[1] == 'modlist' then 247 | return modlist(msg) 248 | end 249 | if matches[1] == 'adminprom' then 250 | if not is_admin(msg) then 251 | return "Only sudo can promote user as admin" 252 | end 253 | local member = string.gsub(matches[2], "@", "") 254 | chat_info(receiver, username_id, {mod_cmd= mod_cmd, receiver=receiver, member=member}) 255 | end 256 | if matches[1] == 'admindem' then 257 | if not is_admin(msg) then 258 | return "Only sudo can promote user as admin" 259 | end 260 | local member = string.gsub(matches[2], "@", "") 261 | chat_info(receiver, username_id, {mod_cmd= mod_cmd, receiver=receiver, member=member}) 262 | end 263 | if matches[1] == 'adminlist' then 264 | if not is_admin(msg) then 265 | return 'Admin only!' 266 | end 267 | return admin_list(msg) 268 | end 269 | if matches[1] == 'chat_add_user' and msg.action.user.id == our_id then 270 | return automodadd(msg) 271 | end 272 | if matches[1] == 'chat_created' and msg.from.id == 0 then 273 | return automodadd(msg) 274 | end 275 | end 276 | 277 | return { 278 | description = "Moderation plugin", 279 | usage = { 280 | moderator = { 281 | "!promote : Promote user as moderator", 282 | "!demote : Demote user from moderator", 283 | "!modlist : List of moderators", 284 | }, 285 | admin = { 286 | "!modadd : Add group to moderation list", 287 | "!modrem : Remove group from moderation list", 288 | }, 289 | sudo = { 290 | "!adminprom : Promote user as admin (must be done from a group)", 291 | "!admindem : Demote user from admin (must be done from a group)", 292 | }, 293 | }, 294 | patterns = { 295 | "^!(modadd)$", 296 | "^!(modrem)$", 297 | "^!(promote) (.*)$", 298 | "^!(demote) (.*)$", 299 | "^!(modlist)$", 300 | "^!(adminprom) (.*)$", -- sudoers only 301 | "^!(admindem) (.*)$", -- sudoers only 302 | "^!(adminlist)$", 303 | "^!!tgservice (chat_add_user)$", 304 | "^!!tgservice (chat_created)$", 305 | }, 306 | run = run, 307 | } 308 | 309 | end 310 | -------------------------------------------------------------------------------- /plugins/pili.lua: -------------------------------------------------------------------------------- 1 | local helpers = require "OAuth.helpers" 2 | 3 | local url = "http://pili.la/api.php" 4 | 5 | local function run(msg, matches) 6 | local url_req = matches[1] 7 | 8 | local request = { 9 | url = url_req 10 | } 11 | 12 | local url = url .. "?" .. helpers.url_encode_arguments(request) 13 | 14 | local res, code = http.request(url) 15 | if code ~= 200 then 16 | return "Sorry, can't connect" 17 | end 18 | 19 | return res 20 | end 21 | 22 | 23 | return { 24 | description = "Shorten an URL with the awesome http://pili.la", 25 | usage = { 26 | "!pili [url]: Shorten the URL" 27 | }, 28 | patterns = { 29 | "^!pili (https?://[%w-_%.%?%.:/%+=&]+)$" 30 | }, 31 | run = run 32 | } 33 | -------------------------------------------------------------------------------- /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 text = '' 26 | local nsum = 0 27 | for k, v in pairs( plugins_names( )) do 28 | -- ✔ enabled, ❌ disabled 29 | local status = '❌' 30 | nsum = nsum+1 31 | nact = 0 32 | -- Check if is enabled 33 | for k2, v2 in pairs(_config.enabled_plugins) do 34 | if v == v2..'.lua' then 35 | status = '✔' 36 | end 37 | nact = nact+1 38 | end 39 | if not only_enabled or status == '✔' then 40 | -- get the name 41 | v = string.match (v, "(.*)%.lua") 42 | text = text..nsum..'. '..v..' '..status..'\n' 43 | end 44 | end 45 | local text = text..'\nThere are '..nsum..' plugins installed.\n'..nact..' plugins enabled and '..nsum-nact..' disabled' 46 | return text 47 | end 48 | 49 | local function list_plugins(only_enabled) 50 | local text = '' 51 | local nsum = 0 52 | for k, v in pairs( plugins_names( )) do 53 | -- ✔ enabled, ❌ disabled 54 | local status = '❌' 55 | nsum = nsum+1 56 | nact = 0 57 | -- Check if is enabled 58 | for k2, v2 in pairs(_config.enabled_plugins) do 59 | if v == v2..'.lua' then 60 | status = '✔' 61 | end 62 | nact = nact+1 63 | end 64 | if not only_enabled or status == '✔' then 65 | -- get the name 66 | v = string.match (v, "(.*)%.lua") 67 | text = text..v..' '..status..'\n' 68 | end 69 | end 70 | local text = text..'\n'..nact..' plugins enabled from '..nsum..' plugins installed.' 71 | return text 72 | end 73 | 74 | local function reload_plugins( ) 75 | plugins = {} 76 | load_plugins() 77 | return list_plugins(true) 78 | end 79 | 80 | 81 | local function enable_plugin( plugin_name ) 82 | print('checking if '..plugin_name..' exists') 83 | -- Check if plugin is enabled 84 | if plugin_enabled(plugin_name) then 85 | return 'Plugin '..plugin_name..' is enabled' 86 | end 87 | -- Checks if plugin exists 88 | if plugin_exists(plugin_name) then 89 | -- Add to the config table 90 | table.insert(_config.enabled_plugins, plugin_name) 91 | print(plugin_name..' added to _config table') 92 | save_config() 93 | -- Reload the plugins 94 | return reload_plugins( ) 95 | else 96 | return 'Plugin '..plugin_name..' does not exists' 97 | end 98 | end 99 | 100 | local function disable_plugin( name, chat ) 101 | -- Check if plugins exists 102 | if not plugin_exists(name) then 103 | return 'Plugin '..name..' does not exists' 104 | end 105 | local k = plugin_enabled(name) 106 | -- Check if plugin is enabled 107 | if not k then 108 | return 'Plugin '..name..' not enabled' 109 | end 110 | -- Disable and reload 111 | table.remove(_config.enabled_plugins, k) 112 | save_config( ) 113 | return reload_plugins(true) 114 | end 115 | 116 | local function disable_plugin_on_chat(receiver, plugin) 117 | if not plugin_exists(plugin) then 118 | return "Plugin doesn't exists" 119 | end 120 | 121 | if not _config.disabled_plugin_on_chat then 122 | _config.disabled_plugin_on_chat = {} 123 | end 124 | 125 | if not _config.disabled_plugin_on_chat[receiver] then 126 | _config.disabled_plugin_on_chat[receiver] = {} 127 | end 128 | 129 | _config.disabled_plugin_on_chat[receiver][plugin] = true 130 | 131 | save_config() 132 | return 'Plugin '..plugin..' disabled on this chat' 133 | end 134 | 135 | local function reenable_plugin_on_chat(receiver, plugin) 136 | if not _config.disabled_plugin_on_chat then 137 | return 'There aren\'t any disabled plugins' 138 | end 139 | 140 | if not _config.disabled_plugin_on_chat[receiver] then 141 | return 'There aren\'t any disabled plugins for this chat' 142 | end 143 | 144 | if not _config.disabled_plugin_on_chat[receiver][plugin] then 145 | return 'This plugin is not disabled' 146 | end 147 | 148 | _config.disabled_plugin_on_chat[receiver][plugin] = false 149 | save_config() 150 | return 'Plugin '..plugin..' is enabled again' 151 | end 152 | 153 | local function run(msg, matches) 154 | -- Show the available plugins 155 | if matches[1] == '!plugins' and is_sudo(msg) then --after changed to moderator mode, set only sudo 156 | return list_all_plugins() 157 | end 158 | 159 | -- Re-enable a plugin for this chat 160 | if matches[1] == 'enable' and matches[3] == 'chat' then 161 | local receiver = get_receiver(msg) 162 | local plugin = matches[2] 163 | print("enable "..plugin..' on this chat') 164 | return reenable_plugin_on_chat(receiver, plugin) 165 | end 166 | 167 | -- Enable a plugin 168 | if matches[1] == 'enable' and is_sudo(msg) then --after changed to moderator mode, set only sudo 169 | local plugin_name = matches[2] 170 | print("enable: "..matches[2]) 171 | return enable_plugin(plugin_name) 172 | end 173 | 174 | -- Disable a plugin on a chat 175 | if matches[1] == 'disable' and matches[3] == 'chat' then 176 | local plugin = matches[2] 177 | local receiver = get_receiver(msg) 178 | print("disable "..plugin..' on this chat') 179 | return disable_plugin_on_chat(receiver, plugin) 180 | end 181 | 182 | -- Disable a plugin 183 | if matches[1] == 'disable' and is_sudo(msg) then --after changed to moderator mode, set only sudo 184 | if matches[2] == 'plugins' then 185 | return 'This plugin can\'t be disabled' 186 | end 187 | print("disable: "..matches[2]) 188 | return disable_plugin(matches[2]) 189 | end 190 | 191 | -- Reload all the plugins! 192 | if matches[1] == 'reload' and is_sudo(msg) then --after changed to moderator mode, set only sudo 193 | return reload_plugins(true) 194 | end 195 | end 196 | 197 | return { 198 | description = "Plugin to manage other plugins. Enable, disable or reload.", 199 | usage = { 200 | moderator = { 201 | "!plugins disable [plugin] chat : disable plugin only this chat.", 202 | "!plugins enable [plugin] chat : enable plugin only this chat.", 203 | }, 204 | sudo = { 205 | "!plugins : list all plugins.", 206 | "!plugins enable [plugin] : enable plugin.", 207 | "!plugins disable [plugin] : disable plugin.", 208 | "!plugins reload : reloads all plugins." }, 209 | }, 210 | patterns = { 211 | "^!plugins$", 212 | "^!plugins? (enable) ([%w_%.%-]+)$", 213 | "^!plugins? (disable) ([%w_%.%-]+)$", 214 | "^!plugins? (enable) ([%w_%.%-]+) (chat)", 215 | "^!plugins? (disable) ([%w_%.%-]+) (chat)", 216 | "^!plugins? (reload)$" }, 217 | run = run, 218 | moderated = true, -- set to moderator mode 219 | --privileged = true 220 | } 221 | 222 | end -------------------------------------------------------------------------------- /plugins/pokedex.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local images_enabled = true; 4 | 5 | local function get_sprite(path) 6 | local url = "http://pokeapi.co/"..path 7 | print(url) 8 | local b,c = http.request(url) 9 | local data = json:decode(b) 10 | local image = data.image 11 | return image 12 | end 13 | 14 | local function callback(extra) 15 | send_msg(extra.receiver, extra.text, ok_cb, false) 16 | end 17 | 18 | local function send_pokemon(query, receiver) 19 | local url = "http://pokeapi.co/api/v1/pokemon/" .. query .. "/" 20 | local b,c = http.request(url) 21 | local pokemon = json:decode(b) 22 | 23 | if pokemon == nil then 24 | return 'No pokémon found.' 25 | end 26 | 27 | -- api returns height and weight x10 28 | local height = tonumber(pokemon.height)/10 29 | local weight = tonumber(pokemon.weight)/10 30 | 31 | local text = 'Pokédex ID: ' .. pokemon.pkdx_id 32 | ..'\nName: ' .. pokemon.name 33 | ..'\nWeight: ' .. weight.." kg" 34 | ..'\nHeight: ' .. height.." m" 35 | ..'\nSpeed: ' .. pokemon.speed 36 | 37 | local image = nil 38 | 39 | if images_enabled and pokemon.sprites and pokemon.sprites[1] then 40 | local sprite = pokemon.sprites[1].resource_uri 41 | image = get_sprite(sprite) 42 | end 43 | 44 | if image then 45 | image = "http://pokeapi.co"..image 46 | local extra = { 47 | receiver = receiver, 48 | text = text 49 | } 50 | send_photo_from_url(receiver, image, callback, extra) 51 | else 52 | return text 53 | end 54 | end 55 | 56 | local function run(msg, matches) 57 | local receiver = get_receiver(msg) 58 | local query = URL.escape(matches[1]) 59 | return send_pokemon(query, receiver) 60 | end 61 | 62 | return { 63 | description = "Pokedex searcher for Telegram", 64 | usage = "!pokedex [Name/ID]: Search the pokédex for Name/ID and get info of the pokémon!", 65 | patterns = { 66 | "^!pokedex (.*)$", 67 | "^!pokemon (.+)$" 68 | }, 69 | run = run 70 | } 71 | 72 | end 73 | -------------------------------------------------------------------------------- /plugins/qr.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | * qr plugin uses: 3 | * - http://goqr.me/api/doc/create-qr-code/ 4 | * psykomantis 5 | ]] 6 | 7 | local function get_hex(str) 8 | local colors = { 9 | red = "f00", 10 | blue = "00f", 11 | green = "0f0", 12 | yellow = "ff0", 13 | purple = "f0f", 14 | white = "fff", 15 | black = "000", 16 | gray = "ccc" 17 | } 18 | 19 | for color, value in pairs(colors) do 20 | if color == str then 21 | return value 22 | end 23 | end 24 | 25 | return str 26 | end 27 | 28 | local function qr(receiver, text, color, bgcolor) 29 | 30 | local url = "http://api.qrserver.com/v1/create-qr-code/?" 31 | .."size=600x600" --fixed size otherways it's low detailed 32 | .."&data="..URL.escape(text:trim()) 33 | 34 | if color then 35 | url = url.."&color="..get_hex(color) 36 | end 37 | if bgcolor then 38 | url = url.."&bgcolor="..get_hex(bgcolor) 39 | end 40 | 41 | local response, code, headers = http.request(url) 42 | 43 | if code ~= 200 then 44 | return "Oops! Error: " .. code 45 | end 46 | 47 | if #response > 0 then 48 | send_photo_from_url(receiver, url) 49 | return 50 | 51 | end 52 | return "Oops! Something strange happened :(" 53 | end 54 | 55 | local function run(msg, matches) 56 | local receiver = get_receiver(msg) 57 | 58 | local text = matches[1] 59 | local color 60 | local back 61 | 62 | if #matches > 1 then 63 | text = matches[3] 64 | color = matches[2] 65 | back = matches[1] 66 | end 67 | 68 | return qr(receiver, text, color, back) 69 | end 70 | 71 | return { 72 | description = {"qr code plugin for telegram, given a text it returns the qr code"}, 73 | usage = { 74 | "!qr [text]", 75 | '!qr "[background color]" "[data color]" [text]\n' 76 | .."Color through text: red|green|blue|purple|black|white|gray\n" 77 | .."Colors through hex notation: (\"a56729\" is brown)\n" 78 | .."Or colors through decimals: (\"255-192-203\" is pink)" 79 | }, 80 | patterns = { 81 | '^!qr "(%w+)" "(%w+)" (.+)$', 82 | "^!qr (.+)$" 83 | }, 84 | run = run 85 | } -------------------------------------------------------------------------------- /plugins/quotes.lua: -------------------------------------------------------------------------------- 1 | local quotes_file = './data/quotes.lua' 2 | local quotes_table 3 | 4 | function read_quotes_file() 5 | local f = io.open(quotes_file, "r+") 6 | 7 | if f == nil then 8 | print ('Created a new quotes file on '..quotes_file) 9 | serialize_to_file({}, quotes_file) 10 | else 11 | print ('Quotes loaded: '..quotes_file) 12 | f:close() 13 | end 14 | return loadfile (quotes_file)() 15 | end 16 | 17 | function save_quote(msg) 18 | local to_id = tostring(msg.to.id) 19 | 20 | if msg.text:sub(11):isempty() then 21 | return "Usage: !addquote quote" 22 | end 23 | 24 | if quotes_table == nil then 25 | quotes_table = {} 26 | end 27 | 28 | if quotes_table[to_id] == nil then 29 | print ('New quote key to_id: '..to_id) 30 | quotes_table[to_id] = {} 31 | end 32 | 33 | local quotes = quotes_table[to_id] 34 | quotes[#quotes+1] = msg.text:sub(11) 35 | 36 | serialize_to_file(quotes_table, quotes_file) 37 | 38 | return "done!" 39 | end 40 | 41 | function get_quote(msg) 42 | local to_id = tostring(msg.to.id) 43 | local quotes_phrases 44 | 45 | quotes_table = read_quotes_file() 46 | quotes_phrases = quotes_table[to_id] 47 | 48 | return quotes_phrases[math.random(1,#quotes_phrases)] 49 | end 50 | 51 | function run(msg, matches) 52 | if string.match(msg.text, "!quote$") then 53 | return get_quote(msg) 54 | elseif string.match(msg.text, "!addquote (.+)$") then 55 | quotes_table = read_quotes_file() 56 | return save_quote(msg) 57 | end 58 | end 59 | 60 | return { 61 | description = "Save quote", 62 | description = "Quote plugin, you can create and retrieve random quotes", 63 | usage = { 64 | "!addquote [msg]", 65 | "!quote", 66 | }, 67 | patterns = { 68 | "^!addquote (.+)$", 69 | "^!quote$", 70 | }, 71 | run = run 72 | } 73 | -------------------------------------------------------------------------------- /plugins/rae.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | function getDulcinea( text ) 4 | -- Powered by https://github.com/javierhonduco/dulcinea 5 | 6 | local api = "http://dulcinea.herokuapp.com/api/?query=" 7 | local query_url = api..text 8 | 9 | local b, code = http.request(query_url) 10 | 11 | if code ~= 200 then 12 | return "Error: HTTP Connection" 13 | end 14 | 15 | dulcinea = json:decode(b) 16 | 17 | if dulcinea.status == "error" then 18 | return "Error: " .. dulcinea.message 19 | end 20 | 21 | while dulcinea.type == "multiple" do 22 | text = dulcinea.response[1].id 23 | b = http.request(api..text) 24 | dulcinea = json:decode(b) 25 | end 26 | 27 | local text = "" 28 | 29 | local responses = #dulcinea.response 30 | 31 | if responses == 0 then 32 | return "Error: 404 word not found" 33 | end 34 | 35 | if (responses > 5) then 36 | responses = 5 37 | end 38 | 39 | for i = 1, responses, 1 do 40 | text = text .. dulcinea.response[i].word .. "\n" 41 | local meanings = #dulcinea.response[i].meanings 42 | if (meanings > 5) then 43 | meanings = 5 44 | end 45 | for j = 1, meanings, 1 do 46 | local meaning = dulcinea.response[i].meanings[j].meaning 47 | text = text .. meaning .. "\n\n" 48 | end 49 | end 50 | 51 | return text 52 | end 53 | 54 | function run(msg, matches) 55 | return getDulcinea(matches[1]) 56 | end 57 | 58 | return { 59 | description = "Spanish dictionary", 60 | usage = "!rae [word]: Search that word in Spanish dictionary.", 61 | patterns = {"^!rae (.*)$"}, 62 | run = run 63 | } 64 | 65 | end -------------------------------------------------------------------------------- /plugins/remind.lua: -------------------------------------------------------------------------------- 1 | local filename='data/remind.lua' 2 | local cronned = load_from_file(filename) 3 | 4 | local function save_cron(msg, text,date) 5 | local origin = get_receiver(msg) 6 | if not cronned[date] then 7 | cronned[date] = {} 8 | end 9 | local arr = { origin, text } ; 10 | table.insert(cronned[date], arr) 11 | serialize_to_file(cronned, filename) 12 | return 'Saved!' 13 | end 14 | 15 | local function delete_cron(date) 16 | for k,v in pairs(cronned) do 17 | if k == date then 18 | cronned[k]=nil 19 | end 20 | end 21 | serialize_to_file(cronned, filename) 22 | end 23 | 24 | local function cron() 25 | for date, values in pairs(cronned) do 26 | if date < os.time() then --time's up 27 | send_msg(values[1][1], "Time's up:\n"..values[1][2], ok_cb, false) 28 | delete_cron(date) --TODO: Maybe check for something else? Like user 29 | end 30 | 31 | end 32 | end 33 | 34 | local function actually_run(msg, delay,text) 35 | if (not delay or not text) then 36 | return "Usage: !remind [delay: 2h3m1s] text" 37 | end 38 | save_cron(msg, text,delay) 39 | return "I'll remind you on " .. os.date("%x at %H:%M:%S",delay) .. " about '" .. text .. "'" 40 | end 41 | 42 | local function run(msg, matches) 43 | local sum = 0 44 | for i = 1, #matches-1 do 45 | local b,_ = string.gsub(matches[i],"[a-zA-Z]","") 46 | if string.find(matches[i], "s") then 47 | sum=sum+b 48 | end 49 | if string.find(matches[i], "m") then 50 | sum=sum+b*60 51 | end 52 | if string.find(matches[i], "h") then 53 | sum=sum+b*3600 54 | end 55 | end 56 | 57 | local date=sum+os.time() 58 | local text = matches[#matches] 59 | 60 | local text = actually_run(msg, date, text) 61 | return text 62 | end 63 | 64 | return { 65 | description = "remind plugin", 66 | usage = { 67 | "!remind [delay: 2hms] text", 68 | "!remind [delay: 2h3m] text", 69 | "!remind [delay: 2h3m1s] text" 70 | }, 71 | patterns = { 72 | "^!remind ([0-9]+[hmsdHMSD]) (.+)$", 73 | "^!remind ([0-9]+[hmsdHMSD])([0-9]+[hmsdHMSD]) (.+)$", 74 | "^!remind ([0-9]+[hmsdHMSD])([0-9]+[hmsdHMSD])([0-9]+[hmsdHMSD]) (.+)$" 75 | }, 76 | run = run, 77 | cron = cron 78 | } 79 | -------------------------------------------------------------------------------- /plugins/roll.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local ROLL_USAGE = "!roll d| d" 4 | local DEFAULT_SIDES = 100 5 | local DEFAULT_NUMBER_OF_DICE = 1 6 | local MAX_NUMBER_OF_DICE = 100 7 | 8 | function run(msg, matches) 9 | local str = matches[1] 10 | local sides = DEFAULT_SIDES 11 | local number_of_dice = DEFAULT_NUMBER_OF_DICE 12 | 13 | if str:find("!roll%s+d%d+%s*$") then 14 | sides = tonumber(str:match("[^d]%d+%s*$")) 15 | elseif str:find("!roll%s+%d+d%d+%s*$") then 16 | number_of_dice = tonumber(str:match("%s+%d+")) 17 | sides = tonumber(str:match("%d+%s*$")) 18 | end 19 | 20 | if number_of_dice > MAX_NUMBER_OF_DICE then 21 | number_of_dice = MAX_NUMBER_OF_DICE 22 | end 23 | 24 | local padding = #string.format("%d", sides) 25 | local results = "" 26 | 27 | local fmt = "%s %0"..padding.."d" 28 | for i=1,number_of_dice do 29 | results = string.format(fmt, results, math.random(sides)) 30 | end 31 | 32 | return string.format("Rolling %dd%d:\n%s", number_of_dice, sides, results) 33 | end 34 | 35 | return { 36 | description = "Roll some dice!", 37 | usage = ROLL_USAGE, 38 | patterns = { 39 | "^!roll%s*.*" 40 | }, 41 | run = run 42 | } 43 | 44 | end 45 | -------------------------------------------------------------------------------- /plugins/rss.lua: -------------------------------------------------------------------------------- 1 | local function get_base_redis(id, option, extra) 2 | local ex = '' 3 | if option ~= nil then 4 | ex = ex .. ':' .. option 5 | if extra ~= nil then 6 | ex = ex .. ':' .. extra 7 | end 8 | end 9 | return 'rss:' .. id .. ex 10 | end 11 | 12 | local function prot_url(url) 13 | local url, h = string.gsub(url, "http://", "") 14 | local url, hs = string.gsub(url, "https://", "") 15 | local protocol = "http" 16 | if hs == 1 then 17 | protocol = "https" 18 | end 19 | return url, protocol 20 | end 21 | 22 | local function get_rss(url, prot) 23 | local res, code = nil, 0 24 | if prot == "http" then 25 | res, code = http.request(url) 26 | elseif prot == "https" then 27 | res, code = https.request(url) 28 | end 29 | if code ~= 200 then 30 | return nil, "Error while doing the petition to " .. url 31 | end 32 | local parsed = feedparser.parse(res) 33 | if parsed == nil then 34 | return nil, "Error decoding the RSS.\nAre you sure that " .. url .. " it's a RSS?" 35 | end 36 | return parsed, nil 37 | end 38 | 39 | local function get_new_entries(last, nentries) 40 | local entries = {} 41 | for k,v in pairs(nentries) do 42 | if v.id == last then 43 | return entries 44 | else 45 | table.insert(entries, v) 46 | end 47 | end 48 | return entries 49 | end 50 | 51 | local function print_subs(id) 52 | local uhash = get_base_redis(id) 53 | local subs = redis:smembers(uhash) 54 | local text = id .. ' are subscribed to:\n---------\n' 55 | for k,v in pairs(subs) do 56 | text = text .. k .. ") " .. v .. '\n' 57 | end 58 | return text 59 | end 60 | 61 | local function subscribe(id, url) 62 | local baseurl, protocol = prot_url(url) 63 | 64 | local prothash = get_base_redis(baseurl, "protocol") 65 | local lasthash = get_base_redis(baseurl, "last_entry") 66 | local lhash = get_base_redis(baseurl, "subs") 67 | local uhash = get_base_redis(id) 68 | 69 | if redis:sismember(uhash, baseurl) then 70 | return "You are already subscribed to " .. url 71 | end 72 | 73 | local parsed, err = get_rss(url, protocol) 74 | if err ~= nil then 75 | return err 76 | end 77 | 78 | local last_entry = "" 79 | if #parsed.entries > 0 then 80 | last_entry = parsed.entries[1].id 81 | end 82 | 83 | local name = parsed.feed.title 84 | 85 | redis:set(prothash, protocol) 86 | redis:set(lasthash, last_entry) 87 | redis:sadd(lhash, id) 88 | redis:sadd(uhash, baseurl) 89 | 90 | return "You had been subscribed to " .. name 91 | end 92 | 93 | local function unsubscribe(id, n) 94 | if #n > 3 then 95 | return "I don't think that you have that many subscriptions." 96 | end 97 | n = tonumber(n) 98 | 99 | local uhash = get_base_redis(id) 100 | local subs = redis:smembers(uhash) 101 | if n < 1 or n > #subs then 102 | return "Subscription id out of range!" 103 | end 104 | local sub = subs[n] 105 | local lhash = get_base_redis(sub, "subs") 106 | 107 | redis:srem(uhash, sub) 108 | redis:srem(lhash, id) 109 | 110 | local left = redis:smembers(lhash) 111 | if #left < 1 then -- no one subscribed, remove it 112 | local prothash = get_base_redis(sub, "protocol") 113 | local lasthash = get_base_redis(sub, "last_entry") 114 | redis:del(prothash) 115 | redis:del(lasthash) 116 | end 117 | 118 | return "You had been unsubscribed from " .. sub 119 | end 120 | 121 | local function cron() 122 | -- sync every 15 mins? 123 | local keys = redis:keys(get_base_redis("*", "subs")) 124 | for k,v in pairs(keys) do 125 | local base = string.match(v, "rss:(.+):subs") -- Get the URL base 126 | local prot = redis:get(get_base_redis(base, "protocol")) 127 | local last = redis:get(get_base_redis(base, "last_entry")) 128 | local url = prot .. "://" .. base 129 | local parsed, err = get_rss(url, prot) 130 | if err ~= nil then 131 | return 132 | end 133 | local newentr = get_new_entries(last, parsed.entries) 134 | local subscribers = {} 135 | local text = '' -- Send only one message with all updates 136 | for k2, v2 in pairs(newentr) do 137 | local title = v2.title or 'No title' 138 | local link = v2.link or v2.id or 'No Link' 139 | text = text .. "[rss](" .. link .. ") - " .. title .. '\n' 140 | end 141 | if text ~= '' then 142 | local newlast = newentr[1].id 143 | redis:set(get_base_redis(base, "last_entry"), newlast) 144 | for k2, receiver in pairs(redis:smembers(v)) do 145 | send_msg(receiver, text, ok_cb, false) 146 | end 147 | end 148 | end 149 | end 150 | 151 | local function run(msg, matches) 152 | local id = "user#id" .. msg.from.id 153 | 154 | if is_chat_msg(msg) then 155 | id = "chat#id" .. msg.to.id 156 | end 157 | 158 | if matches[1] == "!rss"then 159 | return print_subs(id) 160 | end 161 | if matches[1] == "sync" then 162 | if not is_sudo(msg) then 163 | return "Only sudo users can sync the RSS." 164 | end 165 | cron() 166 | end 167 | if matches[1] == "subscribe" or matches[1] == "sub" then 168 | return subscribe(id, matches[2]) 169 | end 170 | 171 | if matches[1] == "unsubscribe" or matches[1] == "uns" then 172 | return unsubscribe(id, matches[2]) 173 | end 174 | end 175 | 176 | 177 | return { 178 | description = "Manage User/Chat RSS subscriptions. If you are in a chat group, the RSS subscriptions will be of that chat. If you are in an one-to-one talk with the bot, the RSS subscriptions will be yours.", 179 | usage = { 180 | "!rss: Get your rss (or chat rss) subscriptions", 181 | "!rss subscribe (url): Subscribe to that url", 182 | "!rss unsubscribe (id): Unsubscribe of that id", 183 | "!rss sync: Download now the updates and send it. Only sudo users can use this option." 184 | }, 185 | patterns = { 186 | "^!rss$", 187 | "^!rss (subscribe) (https?://[%w-_%.%?%.:/%+=&]+)$", 188 | "^!rss (sub) (https?://[%w-_%.%?%.:/%+=&]+)$", 189 | "^!rss (unsubscribe) (%d+)$", 190 | "^!rss (uns) (%d+)$", 191 | "^!rss (sync)$" 192 | }, 193 | run = run, 194 | cron = cron 195 | } 196 | -------------------------------------------------------------------------------- /plugins/search_youtube.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local google_config = load_from_file('data/google.lua') 4 | 5 | local function httpsRequest(url) 6 | print(url) 7 | local res,code = https.request(url) 8 | if code ~= 200 then return nil end 9 | return json:decode(res) 10 | end 11 | 12 | local function searchYoutubeVideos(text) 13 | local url = 'https://www.googleapis.com/youtube/v3/search?' 14 | url = url..'part=snippet'..'&maxResults=4'..'&type=video' 15 | url = url..'&q='..URL.escape(text) 16 | if google_config.api_keys then 17 | local i = math.random(#google_config.api_keys) 18 | local api_key = google_config.api_keys[i] 19 | if api_key then 20 | url = url.."&key="..api_key 21 | end 22 | end 23 | 24 | local data = httpsRequest(url) 25 | 26 | if not data then 27 | print("HTTP Error") 28 | return nil 29 | elseif not data.items then 30 | return nil 31 | end 32 | return data.items 33 | end 34 | 35 | local function run(msg, matches) 36 | local text = '' 37 | local items = searchYoutubeVideos(matches[1]) 38 | if not items then 39 | return "Error!" 40 | end 41 | for k,item in pairs(items) do 42 | text = text..'http://youtu.be/'..item.id.videoId..' '.. 43 | item.snippet.title..'\n\n' 44 | end 45 | return text 46 | end 47 | 48 | return { 49 | description = "Search video on youtube and send it.", 50 | usage = "!youtube [term]: Search for a youtube video and send it.", 51 | patterns = { 52 | "^!youtube (.*)" 53 | }, 54 | run = run 55 | } 56 | 57 | end 58 | -------------------------------------------------------------------------------- /plugins/service_entergroup.lua: -------------------------------------------------------------------------------- 1 | local add_user_cfg = load_from_file('data/add_user_cfg.lua') 2 | 3 | local function template_add_user(base, to_username, from_username, chat_name, chat_id) 4 | base = base or '' 5 | to_username = '@' .. (to_username or '') 6 | from_username = '@' .. (from_username or '') 7 | chat_name = string.gsub(chat_name, '_', ' ') or '' 8 | chat_id = "chat#id" .. (chat_id or '') 9 | if to_username == "@" then 10 | to_username = '' 11 | end 12 | if from_username == "@" then 13 | from_username = '' 14 | end 15 | base = string.gsub(base, "{to_username}", to_username) 16 | base = string.gsub(base, "{from_username}", from_username) 17 | base = string.gsub(base, "{chat_name}", chat_name) 18 | base = string.gsub(base, "{chat_id}", chat_id) 19 | return base 20 | end 21 | 22 | function chat_new_user_link(msg) 23 | local pattern = add_user_cfg.initial_chat_msg 24 | local to_username = msg.from.username 25 | local from_username = 'link (@' .. (msg.action.link_issuer.username or '') .. ')' 26 | local chat_name = msg.to.print_name 27 | local chat_id = msg.to.id 28 | pattern = template_add_user(pattern, to_username, from_username, chat_name, chat_id) 29 | if pattern ~= '' then 30 | local receiver = get_receiver(msg) 31 | send_msg(receiver, pattern, ok_cb, false) 32 | end 33 | end 34 | 35 | function chat_new_user(msg) 36 | local pattern = add_user_cfg.initial_chat_msg 37 | local to_username = msg.action.user.username 38 | local from_username = msg.from.username 39 | local chat_name = msg.to.print_name 40 | local chat_id = msg.to.id 41 | pattern = template_add_user(pattern, to_username, from_username, chat_name, chat_id) 42 | if pattern ~= '' then 43 | local receiver = get_receiver(msg) 44 | send_msg(receiver, pattern, ok_cb, false) 45 | end 46 | end 47 | 48 | local function description_rules(msg, nama) 49 | local data = load_data(_config.moderation.data) 50 | if data[tostring(msg.to.id)] then 51 | local about = "" 52 | local rules = "" 53 | if data[tostring(msg.to.id)]["description"] then 54 | about = data[tostring(msg.to.id)]["description"] 55 | about = "\nDescription :\n"..about.."\n" 56 | end 57 | if data[tostring(msg.to.id)]["rules"] then 58 | rules = data[tostring(msg.to.id)]["rules"] 59 | rules = "\nRules :\n"..rules.."\n" 60 | end 61 | local sambutan = "You are in group '"..string.gsub(msg.to.print_name, "_", " ").."'\n" 62 | local text = sambutan..about..rules.."\n" 63 | local text = text.."Please welcome "..nama 64 | local receiver = get_receiver(msg) 65 | send_large_msg(receiver, text, ok_cb, false) 66 | end 67 | end 68 | 69 | local function run(msg, matches) 70 | if not msg.service then 71 | return "Are you trying to troll me?" 72 | end 73 | --vardump(msg) 74 | if matches[1] == "chat_add_user" then 75 | if not msg.action.user.username then 76 | nama = string.gsub(msg.action.user.print_name, "_", " ") 77 | else 78 | nama = "@"..msg.action.user.username 79 | end 80 | chat_new_user(msg) 81 | description_rules(msg, nama) 82 | elseif matches[1] == "chat_add_user_link" then 83 | if not msg.from.username then 84 | nama = string.gsub(msg.from.print_name, "_", " ") 85 | else 86 | nama = "@"..msg.from.username 87 | end 88 | chat_new_user_link(msg) 89 | description_rules(msg, nama) 90 | elseif matches[1] == "chat_del_user" then 91 | local bye_name = msg.action.user.first_name 92 | return 'Bye '..bye_name..'!' 93 | end 94 | end 95 | 96 | return { 97 | description = "Service plugin that sends a custom message when an user enters a chat.", 98 | usage = "Welcoming new member.", 99 | patterns = { 100 | "^!!tgservice (chat_add_user)$", 101 | "^!!tgservice (chat_add_user_link)$", 102 | "^!!tgservice (chat_del_user)$", 103 | }, 104 | run = run 105 | } 106 | -------------------------------------------------------------------------------- /plugins/service_template.lua: -------------------------------------------------------------------------------- 1 | local function run(msg, matches) 2 | -- avoid this plugins to process user messages 3 | if not msg.service then 4 | -- return "Are you trying to troll me?" 5 | return nil 6 | end 7 | print("Service message received: " .. matches[1]) 8 | end 9 | 10 | 11 | return { 12 | description = "Template for service plugins", 13 | usage = "", 14 | patterns = { 15 | "^!!tgservice (.*)$" -- Do not use the (.*) match in your service plugin 16 | }, 17 | run = run 18 | } 19 | -------------------------------------------------------------------------------- /plugins/set.lua: -------------------------------------------------------------------------------- 1 | local function save_value(msg, name, value) 2 | if (not name or not value) then 3 | return "Usage: !set var_name value" 4 | end 5 | 6 | local hash = nil 7 | if msg.to.type == 'chat' then 8 | hash = 'chat:'..msg.to.id..':variables' 9 | end 10 | if msg.to.type == 'user' then 11 | hash = 'user:'..msg.from.id..':variables' 12 | end 13 | if hash then 14 | redis:hset(hash, name, value) 15 | return "Saved "..name.." => "..value 16 | end 17 | end 18 | 19 | local function run(msg, matches) 20 | local name = string.sub(matches[1], 1, 50) 21 | local value = string.sub(matches[2], 1, 1000) 22 | 23 | local text = save_value(msg, name, value) 24 | return text 25 | end 26 | 27 | return { 28 | description = "Plugin for saving values. get.lua plugin is necessary to retrieve them.", 29 | usage = "!set [value_name] [data]: Saves the data with the value_name name.", 30 | patterns = { 31 | "!set ([^%s]+) (.+)$" 32 | }, 33 | run = run 34 | } 35 | 36 | -------------------------------------------------------------------------------- /plugins/stats.lua: -------------------------------------------------------------------------------- 1 | -- Saves the number of messages from a user 2 | -- Can check the number of messages with !stats 3 | 4 | do 5 | 6 | local NUM_MSG_MAX = 5 7 | local TIME_CHECK = 4 -- seconds 8 | 9 | local function user_print_name(user) 10 | if user.print_name then 11 | return user.print_name 12 | end 13 | 14 | local text = '' 15 | if user.first_name then 16 | text = user.last_name..' ' 17 | end 18 | if user.lastname then 19 | text = text..user.last_name 20 | end 21 | 22 | return text 23 | end 24 | 25 | -- Returns a table with `name` and `msgs` 26 | local function get_msgs_user_chat(user_id, chat_id) 27 | local user_info = {} 28 | local uhash = 'user:'..user_id 29 | local user = redis:hgetall(uhash) 30 | local um_hash = 'msgs:'..user_id..':'..chat_id 31 | user_info.msgs = tonumber(redis:get(um_hash) or 0) 32 | user_info.name = user_print_name(user)..' ('..user_id..')' 33 | return user_info 34 | end 35 | 36 | local function chat_stats(chat_id) 37 | -- Users on chat 38 | local hash = 'chat:'..chat_id..':users' 39 | local users = redis:smembers(hash) 40 | local users_info = {} 41 | 42 | -- Get user info 43 | for i = 1, #users do 44 | local user_id = users[i] 45 | local user_info = get_msgs_user_chat(user_id, chat_id) 46 | table.insert(users_info, user_info) 47 | end 48 | 49 | -- Sort users by msgs number 50 | table.sort(users_info, function(a, b) 51 | if a.msgs and b.msgs then 52 | return a.msgs > b.msgs 53 | end 54 | end) 55 | 56 | local text = '' 57 | for k,user in pairs(users_info) do 58 | text = text..user.name..' => '..user.msgs..'\n' 59 | end 60 | 61 | return text 62 | end 63 | 64 | -- Save stats, ban user 65 | local function pre_process(msg) 66 | -- Ignore service msg 67 | if msg.service then 68 | print('Service message') 69 | return msg 70 | end 71 | 72 | -- Save user on Redis 73 | if msg.from.type == 'user' then 74 | local hash = 'user:'..msg.from.id 75 | print('Saving user', hash) 76 | if msg.from.print_name then 77 | redis:hset(hash, 'print_name', msg.from.print_name) 78 | end 79 | if msg.from.first_name then 80 | redis:hset(hash, 'first_name', msg.from.first_name) 81 | end 82 | if msg.from.last_name then 83 | redis:hset(hash, 'last_name', msg.from.last_name) 84 | end 85 | end 86 | 87 | -- Save stats on Redis 88 | if msg.to.type == 'chat' then 89 | -- User is on chat 90 | local hash = 'chat:'..msg.to.id..':users' 91 | redis:sadd(hash, msg.from.id) 92 | end 93 | 94 | -- Total user msgs 95 | local hash = 'msgs:'..msg.from.id..':'..msg.to.id 96 | redis:incr(hash) 97 | 98 | -- Check flood 99 | if msg.from.type == 'user' then 100 | local hash = 'user:'..msg.from.id..':msgs' 101 | local msgs = tonumber(redis:get(hash) or 0) 102 | if msgs > NUM_MSG_MAX then 103 | print('User '..msg.from.id..'is flooding '..msgs) 104 | msg = nil 105 | end 106 | redis:setex(hash, TIME_CHECK, msgs+1) 107 | end 108 | 109 | return msg 110 | end 111 | 112 | local function bot_stats() 113 | 114 | local redis_scan = [[ 115 | local cursor = '0' 116 | local count = 0 117 | 118 | repeat 119 | local r = redis.call("SCAN", cursor, "MATCH", KEYS[1]) 120 | cursor = r[1] 121 | count = count + #r[2] 122 | until cursor == '0' 123 | return count]] 124 | 125 | -- Users 126 | local hash = 'msgs:*:'..our_id 127 | local r = redis:eval(redis_scan, 1, hash) 128 | local text = 'Users: '..r 129 | 130 | hash = 'chat:*:users' 131 | r = redis:eval(redis_scan, 1, hash) 132 | text = text..'\nChats: '..r 133 | 134 | return text 135 | 136 | end 137 | 138 | local function run(msg, matches) 139 | if matches[1]:lower() == "stats" then 140 | 141 | if not matches[2] then 142 | if msg.to.type == 'chat' then 143 | local chat_id = msg.to.id 144 | return chat_stats(chat_id) 145 | else 146 | return 'Stats works only on chats' 147 | end 148 | end 149 | 150 | if matches[2] == "bot" then 151 | if not is_sudo(msg) then 152 | return "Bot stats requires privileged user" 153 | else 154 | return bot_stats() 155 | end 156 | end 157 | 158 | if matches[2] == "chat" then 159 | if not is_sudo(msg) then 160 | return "This command requires privileged user" 161 | else 162 | return chat_stats(matches[3]) 163 | end 164 | end 165 | end 166 | end 167 | 168 | return { 169 | description = "Plugin to update user stats.", 170 | usage = { 171 | "!stats: Returns a list of Username [telegram_id]: msg_num", 172 | "!stats chat : Show stats for chat_id", 173 | "!stats bot: Shows bot stats (sudo users)" 174 | }, 175 | patterns = { 176 | "^!([Ss]tats)$", 177 | "^!([Ss]tats) (chat) (%d+)", 178 | "^!([Ss]tats) (bot)" 179 | }, 180 | run = run, 181 | pre_process = pre_process 182 | } 183 | 184 | end -------------------------------------------------------------------------------- /plugins/steam.lua: -------------------------------------------------------------------------------- 1 | -- See https://wiki.teamfortress.com/wiki/User:RJackson/StorefrontAPI 2 | 3 | do 4 | 5 | local BASE_URL = 'http://store.steampowered.com/api/appdetails/' 6 | local DESC_LENTH = 200 7 | 8 | local function unescape(str) 9 | str = string.gsub( str, '<', '<' ) 10 | str = string.gsub( str, '>', '>' ) 11 | str = string.gsub( str, '"', '"' ) 12 | str = string.gsub( str, ''', "'" ) 13 | str = string.gsub( str, '&#(%d+);', function(n) return string.char(n) end ) 14 | str = string.gsub( str, '&#x(%d+);', function(n) return string.char(tonumber(n,16)) end ) 15 | str = string.gsub( str, '&', '&' ) -- Be sure to do this after all others 16 | return str 17 | end 18 | 19 | local function get_steam_data (appid) 20 | local url = BASE_URL 21 | url = url..'?appids='..appid 22 | url = url..'&cc=us' 23 | local res,code = http.request(url) 24 | if code ~= 200 then return nil end 25 | local data = json:decode(res)[appid].data 26 | return data 27 | end 28 | 29 | local function price_info (data) 30 | local price = '' -- If no data is empty 31 | 32 | if data then 33 | local initial = data.initial 34 | local final = data.final or data.initial 35 | local min = math.min(data.initial, data.final) 36 | price = tostring(min/100) 37 | if data.discount_percent and initial ~= final then 38 | price = price..data.currency..' ('..data.discount_percent..'% OFF)' 39 | end 40 | price = price..' (US)' 41 | end 42 | 43 | return price 44 | end 45 | 46 | 47 | local function send_steam_data(data, receiver) 48 | local description = string.sub(unescape(data.about_the_game:gsub("%b<>", "")), 1, DESC_LENTH) .. '...' 49 | local title = data.name 50 | local price = price_info(data.price_overview) 51 | 52 | local text = title..' '..price..'\n'..description 53 | local image_url = data.header_image 54 | local cb_extra = { 55 | receiver = receiver, 56 | url = image_url 57 | } 58 | send_msg(receiver, text, send_photo_from_url_callback, cb_extra) 59 | end 60 | 61 | 62 | local function run(msg, matches) 63 | local appid = matches[1] 64 | local data = get_steam_data(appid) 65 | local receiver = get_receiver(msg) 66 | send_steam_data(data, receiver) 67 | end 68 | 69 | return { 70 | description = "Grabs Steam info for Steam links.", 71 | usage = "", 72 | patterns = { 73 | "http://store.steampowered.com/app/([0-9]+)", 74 | }, 75 | run = run 76 | } 77 | 78 | end 79 | -------------------------------------------------------------------------------- /plugins/sudo.lua: -------------------------------------------------------------------------------- 1 | function run_sh(msg) 2 | name = get_name(msg) 3 | text = '' 4 | -- if config.sh_enabled == false then 5 | -- text = '!sh command is disabled' 6 | -- else 7 | -- if is_sudo(msg) then 8 | -- bash = msg.text:sub(4,-1) 9 | -- text = run_bash(bash) 10 | -- else 11 | -- text = name .. ' you have no power here!' 12 | -- end 13 | -- end 14 | if is_sudo(msg) then 15 | bash = msg.text:sub(4,-1) 16 | text = run_bash(bash) 17 | else 18 | text = name .. ' you have no power here!' 19 | end 20 | return text 21 | end 22 | 23 | function run_bash(str) 24 | local cmd = io.popen(str) 25 | local result = cmd:read('*all') 26 | cmd:close() 27 | return result 28 | end 29 | 30 | function on_getting_dialogs(cb_extra,success,result) 31 | if success then 32 | local dialogs={} 33 | for key,value in pairs(result) do 34 | for chatkey, chat in pairs(value.peer) do 35 | print(chatkey,chat) 36 | if chatkey=="id" then 37 | table.insert(dialogs,chat.."\n") 38 | end 39 | if chatkey=="print_name" then 40 | table.insert(dialogs,chat..": ") 41 | end 42 | end 43 | end 44 | 45 | send_msg(cb_extra[1],table.concat(dialogs),ok_cb,false) 46 | end 47 | end 48 | 49 | function run(msg, matches) 50 | if not is_sudo(msg) then 51 | return "You aren't allowed!" 52 | end 53 | local receiver = get_receiver(msg) 54 | if string.match(msg.text, '!sh') then 55 | text = run_sh(msg) 56 | send_msg(receiver, text, ok_cb, false) 57 | return 58 | end 59 | 60 | if string.match(msg.text, '!cpu') then 61 | text = run_bash('uname -snr') .. ' ' .. run_bash('whoami') 62 | text = text .. '\n' .. run_bash('top -b |head -2') 63 | send_msg(receiver, text, ok_cb, false) 64 | return 65 | end 66 | 67 | if matches[1]=="Get dialogs" then 68 | get_dialog_list(on_getting_dialogs,{get_receiver(msg)}) 69 | return 70 | end 71 | end 72 | 73 | return { 74 | description = "shows cpuinfo", 75 | usage = "!cpu", 76 | patterns = {"^!cpu", "^!sh","^Get dialogs$"}, 77 | run = run 78 | } 79 | 80 | -------------------------------------------------------------------------------- /plugins/tex.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local function send_title(cb_extra, success, result) 4 | if success then 5 | send_msg(cb_extra[1], cb_extra[2], ok_cb, false) 6 | end 7 | end 8 | 9 | local function run(msg, matches) 10 | local eq = URL.escape(matches[1]) 11 | 12 | local url = "http://latex.codecogs.com/png.download?" 13 | .."\\dpi{300}%20\\LARGE%20"..eq 14 | 15 | local receiver = get_receiver(msg) 16 | local title = "Edit LaTeX on www.codecogs.com/eqnedit.php?latex="..eq 17 | send_photo_from_url(receiver, url, send_title, {receiver, title}) 18 | end 19 | 20 | return { 21 | description = "Convert LaTeX equation to image", 22 | usage = { 23 | "!tex [equation]: Convert LaTeX equation to image" 24 | }, 25 | patterns = { 26 | "^!tex (.+)$" 27 | }, 28 | run = run 29 | } 30 | 31 | end 32 | 33 | -------------------------------------------------------------------------------- /plugins/time.lua: -------------------------------------------------------------------------------- 1 | -- Implement a command !time [area] which uses 2 | -- 2 Google APIs to get the desired result: 3 | -- 1. Geocoding to get from area to a lat/long pair 4 | -- 2. Timezone to get the local time in that lat/long location 5 | 6 | -- Globals 7 | -- If you have a google api key for the geocoding/timezone api 8 | api_key = nil 9 | 10 | base_api = "https://maps.googleapis.com/maps/api" 11 | dateFormat = "%A %d %B - %H:%M:%S" 12 | 13 | -- Need the utc time for the google api 14 | function utctime() 15 | return os.time(os.date("!*t")) 16 | end 17 | 18 | -- Use the geocoding api to get the lattitude and longitude with accuracy specifier 19 | -- CHECKME: this seems to work without a key?? 20 | function get_latlong(area) 21 | local api = base_api .. "/geocode/json?" 22 | local parameters = "address=".. (URL.escape(area) or "") 23 | if api_key ~= nil then 24 | parameters = parameters .. "&key="..api_key 25 | end 26 | 27 | -- Do the request 28 | local res, code = https.request(api..parameters) 29 | if code ~=200 then return nil end 30 | local data = json:decode(res) 31 | 32 | if (data.status == "ZERO_RESULTS") then 33 | return nil 34 | end 35 | if (data.status == "OK") then 36 | -- Get the data 37 | lat = data.results[1].geometry.location.lat 38 | lng = data.results[1].geometry.location.lng 39 | acc = data.results[1].geometry.location_type 40 | types= data.results[1].types 41 | return lat,lng,acc,types 42 | end 43 | end 44 | 45 | -- Use timezone api to get the time in the lat, 46 | -- Note: this needs an API key 47 | function get_time(lat,lng) 48 | local api = base_api .. "/timezone/json?" 49 | 50 | -- Get a timestamp (server time is relevant here) 51 | local timestamp = utctime() 52 | local parameters = "location=" .. 53 | URL.escape(lat) .. "," .. 54 | URL.escape(lng) .. 55 | "×tamp="..URL.escape(timestamp) 56 | if api_key ~=nil then 57 | parameters = parameters .. "&key="..api_key 58 | end 59 | 60 | local res,code = https.request(api..parameters) 61 | if code ~= 200 then return nil end 62 | local data = json:decode(res) 63 | 64 | if (data.status == "ZERO_RESULTS") then 65 | return nil 66 | end 67 | if (data.status == "OK") then 68 | -- Construct what we want 69 | -- The local time in the location is: 70 | -- timestamp + rawOffset + dstOffset 71 | local localTime = timestamp + data.rawOffset + data.dstOffset 72 | return localTime, data.timeZoneId 73 | end 74 | return localTime 75 | end 76 | 77 | function getformattedLocalTime(area) 78 | if area == nil then 79 | return "The time in nowhere is never" 80 | end 81 | 82 | lat,lng,acc = get_latlong(area) 83 | if lat == nil and lng == nil then 84 | return 'It seems that in "'..area..'" they do not have a concept of time.' 85 | end 86 | local localTime, timeZoneId = get_time(lat,lng) 87 | 88 | return "The local time in "..timeZoneId.." is: ".. os.date(dateFormat,localTime) 89 | end 90 | 91 | function run(msg, matches) 92 | return getformattedLocalTime(matches[1]) 93 | end 94 | 95 | return { 96 | description = "Displays the local time in an area", 97 | usage = "!time [area]: Displays the local time in that area", 98 | patterns = {"^!time (.*)$"}, 99 | run = run 100 | } 101 | -------------------------------------------------------------------------------- /plugins/torrent_search.lua: -------------------------------------------------------------------------------- 1 | --[[ NOT USED DUE TO SSL ERROR 2 | -- See https://getstrike.net/api/ 3 | local function strike_search(query) 4 | local strike_base = 'http://getstrike.net/api/v2/torrents/' 5 | local url = strike_base..'search/?phrase='..URL.escape(query) 6 | print(url) 7 | local b,c = http.request(url) 8 | print(b,c) 9 | local search = json:decode(b) 10 | vardump(search) 11 | 12 | if c ~= 200 then 13 | return search.message 14 | end 15 | 16 | vardump(search) 17 | local results = search.results 18 | local text = 'Results: '..results 19 | local results = math.min(results, 3) 20 | for i=1,results do 21 | local torrent = search.torrents[i] 22 | text = text..torrent.torrent_title 23 | ..'\n'..'Seeds: '..torrent.seeds 24 | ..' '..'Leeches: '..torrent.seeds 25 | ..'\n'..torrent.magnet_uri..'\n\n' 26 | end 27 | return text 28 | end]]-- 29 | 30 | local function search_kickass(query) 31 | local url = 'http://kat.cr/json.php?q='..URL.escape(query) 32 | local b,c = http.request(url) 33 | local data = json:decode(b) 34 | 35 | local text = 'Results: '..data.total_results..'\n\n' 36 | local results = math.min(#data.list, 5) 37 | for i=1,results do 38 | local torrent = data.list[i] 39 | local link = torrent.torrentLink 40 | link = link:gsub('%?title=.+','') 41 | text = text..torrent.title 42 | ..'\n'..'Seeds: '..torrent.seeds 43 | ..' '..'Leeches: '..torrent.leechs 44 | ..'\n'..link 45 | --..'\n magnet:?xt=urn:btih:'..torrent.hash 46 | ..'\n\n' 47 | end 48 | return text 49 | end 50 | 51 | local function run(msg, matches) 52 | local query = matches[1] 53 | return search_kickass(query) 54 | end 55 | 56 | return { 57 | description = "Search Torrents", 58 | usage = "!torrent : Search for torrent", 59 | patterns = { 60 | "^!torrent (.+)$" 61 | }, 62 | run = run 63 | } 64 | -------------------------------------------------------------------------------- /plugins/translate.lua: -------------------------------------------------------------------------------- 1 | 2 | --[[ 3 | -- Translate text using Google Translate. 4 | -- http://translate.google.com/translate_a/single?client=t&ie=UTF-8&oe=UTF-8&hl=en&dt=t&tl=en&sl=auto&text=hello 5 | --]] 6 | do 7 | 8 | function translate(source_lang, target_lang, text) 9 | local path = "http://translate.google.com/translate_a/single" 10 | -- URL query parameters 11 | local params = { 12 | client = "t", 13 | ie = "UTF-8", 14 | oe = "UTF-8", 15 | hl = "en", 16 | dt = "t", 17 | tl = target_lang or "en", 18 | sl = source_lang or "auto", 19 | text = URL.escape(text) 20 | } 21 | 22 | local query = format_http_params(params, true) 23 | local url = path..query 24 | 25 | local res, code = https.request(url) 26 | -- Return nil if error 27 | if code > 200 then return nil end 28 | local trans = res:gmatch("%[%[%[\"(.*)\"")():gsub("\"(.*)", "") 29 | 30 | return trans 31 | end 32 | 33 | function run(msg, matches) 34 | -- Third pattern 35 | if #matches == 1 then 36 | print("First") 37 | local text = matches[1] 38 | return translate(nil, nil, text) 39 | end 40 | 41 | -- Second pattern 42 | if #matches == 2 then 43 | print("Second") 44 | local target = matches[1] 45 | local text = matches[2] 46 | return translate(nil, target, text) 47 | end 48 | 49 | -- First pattern 50 | if #matches == 3 then 51 | print("Third") 52 | local source = matches[1] 53 | local target = matches[2] 54 | local text = matches[3] 55 | return translate(source, target, text) 56 | end 57 | 58 | end 59 | 60 | return { 61 | description = "Translate some text", 62 | usage = { 63 | "!translate text. Translate the text to English.", 64 | "!translate target_lang text.", 65 | "!translate source,target text", 66 | }, 67 | patterns = { 68 | "^!translate ([%w]+),([%a]+) (.+)", 69 | "^!translate ([%w]+) (.+)", 70 | "^!translate (.+)", 71 | }, 72 | run = run 73 | } 74 | 75 | end 76 | -------------------------------------------------------------------------------- /plugins/trivia.lua: -------------------------------------------------------------------------------- 1 | do 2 | -- Trivia plugin developed by Guy Spronck 3 | 4 | -- Returns the chat hash for storing information 5 | local function get_hash(msg) 6 | local hash = nil 7 | if msg.to.type == 'chat' then 8 | hash = 'chat:'..msg.to.id..':trivia' 9 | end 10 | if msg.to.type == 'user' then 11 | hash = 'user:'..msg.from.id..':trivia' 12 | end 13 | return hash 14 | end 15 | 16 | -- Sets the question variables 17 | local function set_question(msg, question, answer) 18 | local hash =get_hash(msg) 19 | if hash then 20 | redis:hset(hash, "question", question) 21 | redis:hset(hash, "answer", answer) 22 | redis:hset(hash, "time", os.time()) 23 | end 24 | end 25 | 26 | -- Returns the current question 27 | local function get_question( msg ) 28 | local hash = get_hash(msg) 29 | if hash then 30 | local question = redis:hget(hash, 'question') 31 | if question ~= "NA" then 32 | return question 33 | end 34 | end 35 | return nil 36 | end 37 | 38 | -- Returns the answer of the last question 39 | local function get_answer(msg) 40 | local hash = get_hash(msg) 41 | if hash then 42 | return redis:hget(hash, 'answer') 43 | else 44 | return nil 45 | end 46 | end 47 | 48 | -- Returns the time of the last question 49 | local function get_time(msg) 50 | local hash = get_hash(msg) 51 | if hash then 52 | return redis:hget(hash, 'time') 53 | else 54 | return nil 55 | end 56 | end 57 | 58 | -- This function generates a new question if available 59 | local function get_newquestion(msg) 60 | local timediff = 601 61 | if(get_time(msg)) then 62 | timediff = os.time() - get_time(msg) 63 | end 64 | if(timediff > 600 or get_question(msg) == nil)then 65 | -- Let's show the answer if no-body guessed it right. 66 | if(get_question(msg)) then 67 | send_large_msg(get_receiver(msg), "The question '" .. get_question(msg) .."' has not been answered. \nThe answer was '" .. get_answer(msg) .."'") 68 | end 69 | 70 | local url = "http://jservice.io/api/random/" 71 | local b,c = http.request(url) 72 | local query = json:decode(b) 73 | 74 | if query then 75 | local stringQuestion = "" 76 | if(query[1].category)then 77 | stringQuestion = "Category: " .. query[1].category.title .. "\n" 78 | end 79 | if query[1].question then 80 | stringQuestion = stringQuestion .. "Question: " .. query[1].question 81 | set_question(msg, query[1].question, query[1].answer:lower()) 82 | return stringQuestion 83 | end 84 | end 85 | return 'Something went wrong, please try again.' 86 | else 87 | return 'Please wait ' .. 600 - timediff .. ' seconds before requesting a new question. \nUse !triviaquestion to see the current question.' 88 | end 89 | end 90 | 91 | -- This function generates a new question when forced 92 | local function force_newquestion(msg) 93 | -- Let's show the answer if no-body guessed it right. 94 | if(get_question(msg)) then 95 | send_large_msg(get_receiver(msg), "The question '" .. get_question(msg) .."' has not been answered. \nThe answer was '" .. get_answer(msg) .."'") 96 | end 97 | 98 | local url = "http://jservice.io/api/random/" 99 | local b,c = http.request(url) 100 | local query = json:decode(b) 101 | 102 | if query then 103 | local stringQuestion = "" 104 | if(query[1].category)then 105 | stringQuestion = "Category: " .. query[1].category.title .. "\n" 106 | end 107 | if query[1].question then 108 | stringQuestion = stringQuestion .. "Question: " .. query[1].question 109 | set_question(msg, query[1].question, query[1].answer:lower()) 110 | return stringQuestion 111 | end 112 | end 113 | return 'Something went wrong, please try again.' 114 | end 115 | 116 | -- This function adds a point to the player 117 | local function give_point(msg) 118 | local hash = get_hash(msg) 119 | if hash then 120 | local score = tonumber(redis:hget(hash, msg.from.id) or 0) 121 | redis:hset(hash, msg.from.id, score+1) 122 | end 123 | end 124 | 125 | -- This function checks for a correct answer 126 | local function check_answer(msg, answer) 127 | if(get_answer(msg)) then -- Safety for first time use 128 | if(get_answer(msg) == "NA")then 129 | -- Question has not been set, give a new one 130 | --get_newquestion(msg) 131 | return "No question set, please use !trivia first." 132 | elseif (get_answer(msg) == answer:lower()) then -- Question is set, lets check the answer 133 | set_question(msg, "NA", "NA") -- Correct, clear the answer 134 | give_point(msg) -- gives out point to player for correct answer 135 | return msg.from.print_name .. " has answered correctly! \nUse !trivia to get a new question." 136 | else 137 | return "Sorry " .. msg.from.print_name .. ", but '" .. answer .. "' is not the correct answer!" 138 | end 139 | else 140 | return "No question set, please use !trivia first." 141 | end 142 | end 143 | 144 | local function user_print_name(user) 145 | if user.print_name then 146 | return user.print_name 147 | end 148 | 149 | local text = '' 150 | if user.first_name then 151 | text = user.last_name..' ' 152 | end 153 | if user.lastname then 154 | text = text..user.last_name 155 | end 156 | 157 | return text 158 | end 159 | 160 | 161 | local function get_user_score(msg, user_id, chat_id) 162 | local user_info = {} 163 | local uhash = 'user:'..user_id 164 | local user = redis:hgetall(uhash) 165 | local hash = 'chat:'..msg.to.id..':trivia' 166 | user_info.score = tonumber(redis:hget(hash, user_id) or 0) 167 | user_info.name = user_print_name(user)..' ('..user_id..')' 168 | return user_info 169 | end 170 | 171 | -- Function to print score 172 | local function trivia_scores(msg) 173 | if msg.to.type == 'chat' then 174 | -- Users on chat 175 | local hash = 'chat:'..msg.to.id..':users' 176 | local users = redis:smembers(hash) 177 | local users_info = {} 178 | 179 | -- Get user info 180 | for i = 1, #users do 181 | local user_id = users[i] 182 | local user_info = get_user_score(msg, user_id, msg.to.id) 183 | table.insert(users_info, user_info) 184 | end 185 | 186 | table.sort(users_info, function(a, b) 187 | if a.score and b.score then 188 | return a.score > b.score 189 | end 190 | end) 191 | 192 | local text = '' 193 | for k,user in pairs(users_info) do 194 | text = text..user.name..' => '..user.score..'\n' 195 | end 196 | 197 | return text 198 | else 199 | return "This function is only available in group chats." 200 | end 201 | end 202 | 203 | local function run(msg, matches) 204 | if(matches[1] == "!triviascore" or matches[1] == "!triviascores") then 205 | -- Output all scores 206 | return trivia_scores(msg) 207 | elseif(matches[1] == "!triviaquestion")then 208 | return "Question: " .. get_question(msg) 209 | elseif(matches[1] == "!triviaskip") then 210 | if is_sudo(msg) then 211 | return force_newquestion(msg) 212 | end 213 | elseif(matches[1] ~= "!trivia") then 214 | return check_answer(msg, matches[1]) 215 | end 216 | 217 | return get_newquestion(msg) 218 | end 219 | 220 | return { 221 | description = "Trivia plugin for Telegram", 222 | usage = { 223 | "!trivia to obtain a new question.", 224 | "!trivia [answer] to answer the question.", 225 | "!triviaquestion to show the current question.", 226 | "!triviascore to get a scoretable of all players.", 227 | "!triviaskip to skip a question (requires sudo)" 228 | }, 229 | patterns = {"^!trivia (.*)$", 230 | "^!trivia$", 231 | "^!triviaquestion$", 232 | "^!triviascore$", 233 | "^!triviascores$", 234 | "^!triviaskip$"}, 235 | run = run 236 | } 237 | 238 | end 239 | -------------------------------------------------------------------------------- /plugins/tweet.lua: -------------------------------------------------------------------------------- 1 | local OAuth = require "OAuth" 2 | 3 | local consumer_key = "" 4 | local consumer_secret = "" 5 | local access_token = "" 6 | local access_token_secret = "" 7 | 8 | local twitter_url = "https://api.twitter.com/1.1/statuses/user_timeline.json" 9 | 10 | local client = OAuth.new(consumer_key, 11 | consumer_secret, 12 | { RequestToken = "https://api.twitter.com/oauth/request_token", 13 | AuthorizeUser = {"https://api.twitter.com/oauth/authorize", method = "GET"}, 14 | AccessToken = "https://api.twitter.com/oauth/access_token"}, 15 | { OAuthToken = access_token, 16 | OAuthTokenSecret = access_token_secret}) 17 | 18 | 19 | local function send_generics_from_url_callback(cb_extra, success, result) 20 | -- cb_extra is a table containing receiver, urls and remove_path 21 | local receiver = cb_extra.receiver 22 | local urls = cb_extra.urls 23 | local remove_path = cb_extra.remove_path 24 | local f = cb_extra.func 25 | 26 | -- The previously image to remove 27 | if remove_path ~= nil then 28 | os.remove(remove_path) 29 | print("Deleted: "..remove_path) 30 | end 31 | 32 | -- Nil or empty, exit case (no more urls) 33 | if urls == nil or #urls == 0 then 34 | return false 35 | end 36 | 37 | -- Take the head and remove from urls table 38 | local head = table.remove(urls, 1) 39 | 40 | local file_path = download_to_file(head, false) 41 | local cb_extra = { 42 | receiver = receiver, 43 | urls = urls, 44 | remove_path = file_path, 45 | func = f 46 | } 47 | 48 | -- Send first and postpone the others as callback 49 | f(receiver, file_path, send_generics_from_url_callback, cb_extra) 50 | end 51 | 52 | local function send_generics_from_url(f, receiver, urls) 53 | local cb_extra = { 54 | receiver = receiver, 55 | urls = urls, 56 | remove_path = nil, 57 | func = f 58 | } 59 | send_generics_from_url_callback(cb_extra) 60 | end 61 | 62 | local function send_gifs_from_url(receiver, urls) 63 | send_generics_from_url(send_document, receiver, urls) 64 | end 65 | 66 | local function send_videos_from_url(receiver, urls) 67 | send_generics_from_url(send_video, receiver, urls) 68 | end 69 | 70 | local function send_all_files(receiver, urls) 71 | local data = { 72 | images = { 73 | func = send_photos_from_url, 74 | urls = {} 75 | }, 76 | gifs = { 77 | func = send_gifs_from_url, 78 | urls = {} 79 | }, 80 | videos = { 81 | func = send_videos_from_url, 82 | urls = {} 83 | } 84 | } 85 | 86 | local table_to_insert = nil 87 | for i,url in pairs(urls) do 88 | local _, _, extension = string.match(url, "(https?)://([^\\]-([^\\%.]+))$") 89 | local mime_type = mimetype.get_content_type_no_sub(extension) 90 | if extension == 'gif' then 91 | table_to_insert = data.gifs.urls 92 | elseif mime_type == 'image' then 93 | table_to_insert = data.images.urls 94 | elseif mime_type == 'video' then 95 | table_to_insert = data.videos.urls 96 | else 97 | table_to_insert = nil 98 | end 99 | if table_to_insert then 100 | table.insert(table_to_insert, url) 101 | end 102 | end 103 | for k, v in pairs(data) do 104 | if #v.urls > 0 then 105 | end 106 | v.func(receiver, v.urls) 107 | end 108 | end 109 | 110 | local function check_keys() 111 | if consumer_key:isempty() then 112 | return "Twitter Consumer Key is empty, write it in plugins/tweet.lua" 113 | end 114 | if consumer_secret:isempty() then 115 | return "Twitter Consumer Secret is empty, write it in plugins/tweet.lua" 116 | end 117 | if access_token:isempty() then 118 | return "Twitter Access Token is empty, write it in plugins/tweet.lua" 119 | end 120 | if access_token_secret:isempty() then 121 | return "Twitter Access Token Secret is empty, write it in plugins/tweet.lua" 122 | end 123 | return "" 124 | end 125 | 126 | 127 | local function analyze_tweet(tweet) 128 | local header = "Tweet from " .. tweet.user.name .. " (@" .. tweet.user.screen_name .. ")\n" -- "Link: https://twitter.com/statuses/" .. tweet.id_str 129 | local text = tweet.text 130 | 131 | -- replace short URLs 132 | if tweet.entities.url then 133 | for k, v in pairs(tweet.entities.urls) do 134 | local short = v.url 135 | local long = v.expanded_url 136 | text = text:gsub(short, long) 137 | end 138 | end 139 | 140 | -- remove urls 141 | local urls = {} 142 | if tweet.extended_entities and tweet.extended_entities.media then 143 | for k, v in pairs(tweet.extended_entities.media) do 144 | if v.video_info and v.video_info.variants then -- If it's a video! 145 | table.insert(urls, v.video_info.variants[1].url) 146 | else -- If not, is an image 147 | table.insert(urls, v.media_url) 148 | end 149 | text = text:gsub(v.url, "") -- Replace the URL in text 150 | end 151 | end 152 | 153 | return header, text, urls 154 | end 155 | 156 | 157 | local function sendTweet(receiver, tweet) 158 | local header, text, urls = analyze_tweet(tweet) 159 | -- send the parts 160 | send_msg(receiver, header .. "\n" .. text, ok_cb, false) 161 | send_all_files(receiver, urls) 162 | return nil 163 | end 164 | 165 | 166 | local function getTweet(msg, base, all) 167 | local receiver = get_receiver(msg) 168 | 169 | local response_code, response_headers, response_status_line, response_body = client:PerformRequest("GET", twitter_url, base) 170 | 171 | if response_code ~= 200 then 172 | return "Can't connect, maybe the user doesn't exist." 173 | end 174 | 175 | local response = json:decode(response_body) 176 | if #response == 0 then 177 | return "Can't retrieve any tweets, sorry" 178 | end 179 | if all then 180 | for i,tweet in pairs(response) do 181 | sendTweet(receiver, tweet) 182 | end 183 | else 184 | local i = math.random(#response) 185 | local tweet = response[i] 186 | sendTweet(receiver, tweet) 187 | end 188 | 189 | return nil 190 | end 191 | 192 | function isint(n) 193 | return n==math.floor(n) 194 | end 195 | 196 | local function run(msg, matches) 197 | local checked = check_keys() 198 | if not checked:isempty() then 199 | return checked 200 | end 201 | 202 | local base = {include_rts = 1} 203 | 204 | if matches[1] == 'id' then 205 | local userid = tonumber(matches[2]) 206 | if userid == nil or not isint(userid) then 207 | return "The id of a user is a number, check this web: http://gettwitterid.com/" 208 | end 209 | base.user_id = userid 210 | elseif matches[1] == 'name' then 211 | base.screen_name = matches[2] 212 | else 213 | return "" 214 | end 215 | 216 | local count = 200 217 | local all = false 218 | if #matches > 2 and matches[3] == 'last' then 219 | count = 1 220 | if #matches == 4 then 221 | local n = tonumber(matches[4]) 222 | if n > 10 then 223 | return "You only can ask for 10 tweets at most" 224 | end 225 | count = matches[4] 226 | all = true 227 | end 228 | end 229 | base.count = count 230 | 231 | return getTweet(msg, base, all) 232 | end 233 | 234 | 235 | return { 236 | description = "Random tweet from user", 237 | usage = { 238 | "!tweet id [id]: Get a random tweet from the user with that ID", 239 | "!tweet id [id] last: Get a random tweet from the user with that ID", 240 | "!tweet name [name]: Get a random tweet from the user with that name", 241 | "!tweet name [name] last: Get a random tweet from the user with that name" 242 | }, 243 | patterns = { 244 | "^!tweet (id) ([%w_%.%-]+)$", 245 | "^!tweet (id) ([%w_%.%-]+) (last)$", 246 | "^!tweet (id) ([%w_%.%-]+) (last) ([%d]+)$", 247 | "^!tweet (name) ([%w_%.%-]+)$", 248 | "^!tweet (name) ([%w_%.%-]+) (last)$", 249 | "^!tweet (name) ([%w_%.%-]+) (last) ([%d]+)$" 250 | }, 251 | run = run 252 | } 253 | -------------------------------------------------------------------------------- /plugins/twitter.lua: -------------------------------------------------------------------------------- 1 | local OAuth = require "OAuth" 2 | 3 | -- EDIT data/twitter.lua with the API keys 4 | local twitter_config = load_from_file('data/twitter.lua', { 5 | -- DON'T EDIT HERE. 6 | consumer_key = "", consumer_secret = "", 7 | access_token = "", access_token_secret = "" 8 | }) 9 | 10 | local client = OAuth.new(twitter_config.consumer_key, twitter_config.consumer_secret, { 11 | RequestToken = "https://api.twitter.com/oauth/request_token", 12 | AuthorizeUser = {"https://api.twitter.com/oauth/authorize", method = "GET"}, 13 | AccessToken = "https://api.twitter.com/oauth/twitter_config.access_token" 14 | }, { 15 | OAuthToken = twitter_config.access_token, 16 | OAuthTokenSecret = twitter_config.access_token_secret 17 | }) 18 | 19 | function run(msg, matches) 20 | 21 | if twitter_config.consumer_key:isempty() then 22 | return "Twitter Consumer Key is empty, write it in plugins/twitter.lua" 23 | end 24 | if twitter_config.consumer_secret:isempty() then 25 | return "Twitter Consumer Secret is empty, write it in plugins/twitter.lua" 26 | end 27 | if twitter_config.access_token:isempty() then 28 | return "Twitter Access Token is empty, write it in plugins/twitter.lua" 29 | end 30 | if twitter_config.access_token_secret:isempty() then 31 | return "Twitter Access Token Secret is empty, write it in plugins/twitter.lua" 32 | end 33 | 34 | local twitter_url = "https://api.twitter.com/1.1/statuses/show/" .. matches[1] .. ".json" 35 | local response_code, response_headers, response_status_line, response_body = client:PerformRequest("GET", twitter_url) 36 | local response = json:decode(response_body) 37 | 38 | local header = "Tweet from " .. response.user.name .. " (@" .. response.user.screen_name .. ")\n" 39 | local text = response.text 40 | 41 | -- replace short URLs 42 | if response.entities.url then 43 | for k, v in pairs(response.entities.urls) do 44 | local short = v.url 45 | local long = v.expanded_url 46 | text = text:gsub(short, long) 47 | end 48 | end 49 | 50 | -- remove images 51 | local images = {} 52 | if response.extended_entities and response.extended_entities.media then 53 | for k, v in pairs(response.extended_entities.media) do 54 | local url = v.url 55 | local pic = v.media_url 56 | text = text:gsub(url, "") 57 | table.insert(images, pic) 58 | end 59 | end 60 | 61 | -- send the parts 62 | local receiver = get_receiver(msg) 63 | send_msg(receiver, header .. "\n" .. text, ok_cb, false) 64 | send_photos_from_url(receiver, images) 65 | return nil 66 | end 67 | 68 | 69 | return { 70 | description = "When user sends twitter URL, send text and images to origin. Requires OAuth Key.", 71 | usage = "", 72 | patterns = { 73 | "https://twitter.com/[^/]+/status/([0-9]+)" 74 | }, 75 | run = run 76 | } 77 | -------------------------------------------------------------------------------- /plugins/twitter_send.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local OAuth = require "OAuth" 4 | 5 | local consumer_key = "" 6 | local consumer_secret = "" 7 | local access_token = "" 8 | local access_token_secret = "" 9 | 10 | local client = OAuth.new(consumer_key, consumer_secret, { 11 | RequestToken = "https://api.twitter.com/oauth/request_token", 12 | AuthorizeUser = {"https://api.twitter.com/oauth/authorize", method = "GET"}, 13 | AccessToken = "https://api.twitter.com/oauth/access_token" 14 | }, { 15 | OAuthToken = access_token, 16 | OAuthTokenSecret = access_token_secret 17 | }) 18 | 19 | function run(msg, matches) 20 | if consumer_key:isempty() then 21 | return "Twitter Consumer Key is empty, write it in plugins/twitter_send.lua" 22 | end 23 | if consumer_secret:isempty() then 24 | return "Twitter Consumer Secret is empty, write it in plugins/twitter_send.lua" 25 | end 26 | if access_token:isempty() then 27 | return "Twitter Access Token is empty, write it in plugins/twitter_send.lua" 28 | end 29 | if access_token_secret:isempty() then 30 | return "Twitter Access Token Secret is empty, write it in plugins/twitter_send.lua" 31 | end 32 | 33 | if not is_sudo(msg) then 34 | return "You aren't allowed to send tweets" 35 | end 36 | 37 | local response_code, response_headers, response_status_line, response_body = 38 | client:PerformRequest("POST", "https://api.twitter.com/1.1/statuses/update.json", { 39 | status = matches[1] 40 | }) 41 | if response_code ~= 200 then 42 | return "Error: "..response_code 43 | end 44 | return "Tweet sent" 45 | end 46 | 47 | return { 48 | description = "Sends a tweet", 49 | usage = "!tw [text]: Sends the Tweet with the configured account.", 50 | patterns = {"^!tw (.+)"}, 51 | run = run 52 | } 53 | 54 | end 55 | -------------------------------------------------------------------------------- /plugins/version.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | function run(msg, matches) 4 | return 'Telegram Bot '.. VERSION .. [[ 5 | Checkout http://git.io/vO30o 6 | GNU GPL v2 license. 7 | @silenceangel for more info.]] 8 | end 9 | 10 | return { 11 | description = "Shows bot version", 12 | usage = "!version: Shows bot version", 13 | patterns = { 14 | "^!version$" 15 | }, 16 | run = run 17 | } 18 | 19 | end 20 | -------------------------------------------------------------------------------- /plugins/vote.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local _file_votes = './data/votes.lua' 4 | 5 | function read_file_votes () 6 | local f = io.open(_file_votes, "r+") 7 | if f == nil then 8 | print ('Created voting file '.._file_votes) 9 | serialize_to_file({}, _file_votes) 10 | else 11 | print ('Values loaded: '.._file_votes) 12 | f:close() 13 | end 14 | return loadfile (_file_votes)() 15 | end 16 | 17 | function clear_votes (chat) 18 | local _votes = read_file_votes () 19 | _votes [chat] = {} 20 | serialize_to_file(_votes, _file_votes) 21 | end 22 | 23 | function votes_result (chat) 24 | local _votes = read_file_votes () 25 | local results = {} 26 | local result_string = "" 27 | if _votes [chat] == nil then 28 | _votes[chat] = {} 29 | end 30 | for user,vote in pairs (_votes[chat]) do 31 | if (results [vote] == nil) then 32 | results [vote] = user 33 | else 34 | results [vote] = results [vote] .. ", " .. user 35 | end 36 | end 37 | for vote,users in pairs (results) do 38 | result_string = result_string .. vote .. " : " .. users .. "\n" 39 | end 40 | return result_string 41 | end 42 | 43 | 44 | function save_vote(chat, user, vote) 45 | local _votes = read_file_votes () 46 | if _votes[chat] == nil then 47 | _votes[chat] = {} 48 | end 49 | _votes[chat][user] = vote 50 | 51 | serialize_to_file(_votes, _file_votes) 52 | 53 | end 54 | 55 | function run(msg, matches) 56 | if (matches[1] == "ing") then 57 | if (matches [2] == "reset") then 58 | clear_votes (tostring(msg.to.id)) 59 | return "Voting statistics reset.." 60 | elseif (matches [2] == "stats") then 61 | local votes_result = votes_result (tostring(msg.to.id)) 62 | if (votes_result == "") then 63 | votes_result = "[No votes registered]\n" 64 | end 65 | return "Voting statistics :\n" .. votes_result 66 | end 67 | else 68 | save_vote(tostring(msg.to.id), msg.from.print_name, tostring(tonumber(matches[2]))) 69 | return "Vote registered : " .. msg.from.print_name .. " " .. tostring(tonumber(matches [2])) 70 | end 71 | end 72 | 73 | return { 74 | description = "Plugin for voting in groups.", 75 | usage = { 76 | "!voting reset: Reset all the votes.", 77 | "!vote [number]: Cast the vote.", 78 | "!voting stats: Shows the statistics of voting." 79 | }, 80 | patterns = { 81 | "^!vot(ing) (reset)", 82 | "^!vot(ing) (stats)", 83 | "^!vot(e) ([0-9]+)$" 84 | }, 85 | run = run 86 | } 87 | 88 | end -------------------------------------------------------------------------------- /plugins/weather.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local BASE_URL = "http://api.openweathermap.org/data/2.5/weather" 4 | 5 | local function get_weather(location) 6 | print("Finding weather in ", location) 7 | local url = BASE_URL 8 | url = url..'?q='..location 9 | url = url..'&units=metric' 10 | 11 | local b, c, h = http.request(url) 12 | if c ~= 200 then return nil end 13 | 14 | local weather = json:decode(b) 15 | local city = weather.name 16 | local country = weather.sys.country 17 | local temp = 'The temperature in '..city 18 | ..' (' ..country..')' 19 | ..' is '..weather.main.temp..'°C' 20 | local conditions = 'Current conditions are: ' 21 | .. weather.weather[1].description 22 | 23 | if weather.weather[1].main == 'Clear' then 24 | conditions = conditions .. ' ☀' 25 | elseif weather.weather[1].main == 'Clouds' then 26 | conditions = conditions .. ' ☁☁' 27 | elseif weather.weather[1].main == 'Rain' then 28 | conditions = conditions .. ' ☔' 29 | elseif weather.weather[1].main == 'Thunderstorm' then 30 | conditions = conditions .. ' ☔☔☔☔' 31 | end 32 | 33 | return temp .. '\n' .. conditions 34 | end 35 | 36 | local function run(msg, matches) 37 | local city = 'Yogyakarta' 38 | 39 | if matches[1] ~= '!weather' then 40 | city = matches[1] 41 | end 42 | local text = get_weather(city) 43 | if not text then 44 | text = 'Can\'t get weather from that city.' 45 | end 46 | return text 47 | end 48 | 49 | return { 50 | description = "weather in that city (Yogyakarta is default)", 51 | usage = "!weather (city)", 52 | patterns = { 53 | "^!weather$", 54 | "^!weather (.*)$" 55 | }, 56 | run = run 57 | } 58 | 59 | end -------------------------------------------------------------------------------- /plugins/webshot.lua: -------------------------------------------------------------------------------- 1 | local helpers = require "OAuth.helpers" 2 | 3 | local base = 'https://screenshotmachine.com/' 4 | local url = base .. 'processor.php' 5 | 6 | local function get_webshot_url(param) 7 | local response_body = {} 8 | local request_constructor = { 9 | url = url, 10 | method = "GET", 11 | sink = ltn12.sink.table(response_body), 12 | headers = { 13 | referer = base, 14 | dnt = "1", 15 | origin = base, 16 | ["User-Agent"] = "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36" 17 | }, 18 | redirect = false 19 | } 20 | 21 | local arguments = { 22 | urlparam = param, 23 | size = "FULL" 24 | } 25 | 26 | request_constructor.url = url .. "?" .. helpers.url_encode_arguments(arguments) 27 | 28 | local ok, response_code, response_headers, response_status_line = https.request(request_constructor) 29 | if not ok or response_code ~= 200 then 30 | return nil 31 | end 32 | 33 | local response = table.concat(response_body) 34 | return string.match(response, "href='(.-)'") 35 | end 36 | 37 | local function run(msg, matches) 38 | local find = get_webshot_url(matches[1]) 39 | if find then 40 | local imgurl = base .. find 41 | local receiver = get_receiver(msg) 42 | send_photo_from_url(receiver, imgurl) 43 | end 44 | end 45 | 46 | 47 | return { 48 | description = "Send an screenshot of a website.", 49 | usage = { 50 | "!webshot [url]: Take an screenshot of the web and send it back to you." 51 | }, 52 | patterns = { 53 | "^!webshot (https?://[%w-_%.%?%.:/%+=&]+)$", 54 | }, 55 | run = run 56 | } 57 | -------------------------------------------------------------------------------- /plugins/wiki.lua: -------------------------------------------------------------------------------- 1 | -- http://git.io/vUA4M 2 | local socket = require "socket" 3 | local JSON = require "cjson" 4 | 5 | local wikiusage = { 6 | "!wiki [text]: Read extract from default Wikipedia (EN)", 7 | "!wiki(lang) [text]: Read extract from 'lang' Wikipedia. Example: !wikies hola", 8 | "!wiki search [text]: Search articles on default Wikipedia (EN)", 9 | "!wiki(lang) search [text]: Search articles on 'lang' Wikipedia. Example: !wikies search hola", 10 | } 11 | 12 | local Wikipedia = { 13 | -- http://meta.wikimedia.org/wiki/List_of_Wikipedias 14 | wiki_server = "https://%s.wikipedia.org", 15 | wiki_path = "/w/api.php", 16 | wiki_load_params = { 17 | action = "query", 18 | prop = "extracts", 19 | format = "json", 20 | exchars = 300, 21 | exsectionformat = "plain", 22 | explaintext = "", 23 | redirects = "" 24 | }, 25 | wiki_search_params = { 26 | action = "query", 27 | list = "search", 28 | srlimit = 20, 29 | format = "json", 30 | }, 31 | default_lang = "en", 32 | } 33 | 34 | function Wikipedia:getWikiServer(lang) 35 | return string.format(self.wiki_server, lang or self.default_lang) 36 | end 37 | 38 | --[[ 39 | -- return decoded JSON table from Wikipedia 40 | --]] 41 | function Wikipedia:loadPage(text, lang, intro, plain, is_search) 42 | local request, sink = {}, {} 43 | local query = "" 44 | local parsed 45 | 46 | if is_search then 47 | for k,v in pairs(self.wiki_search_params) do 48 | query = query .. k .. '=' .. v .. '&' 49 | end 50 | parsed = URL.parse(self:getWikiServer(lang)) 51 | parsed.path = self.wiki_path 52 | parsed.query = query .. "srsearch=" .. URL.escape(text) 53 | else 54 | self.wiki_load_params.explaintext = plain and "" or nil 55 | for k,v in pairs(self.wiki_load_params) do 56 | query = query .. k .. '=' .. v .. '&' 57 | end 58 | parsed = URL.parse(self:getWikiServer(lang)) 59 | parsed.path = self.wiki_path 60 | parsed.query = query .. "titles=" .. URL.escape(text) 61 | end 62 | 63 | -- HTTP request 64 | request['url'] = URL.build(parsed) 65 | print(request['url']) 66 | request['method'] = 'GET' 67 | request['sink'] = ltn12.sink.table(sink) 68 | 69 | local httpRequest = parsed.scheme == 'http' and http.request or https.request 70 | local code, headers, status = socket.skip(1, httpRequest(request)) 71 | 72 | if not headers or not sink then 73 | return nil 74 | end 75 | 76 | local content = table.concat(sink) 77 | if content ~= "" then 78 | local ok, result = pcall(JSON.decode, content) 79 | if ok and result then 80 | return result 81 | else 82 | return nil 83 | end 84 | else 85 | return nil 86 | end 87 | end 88 | 89 | -- extract intro passage in wiki page 90 | function Wikipedia:wikintro(text, lang) 91 | local result = self:loadPage(text, lang, true, true) 92 | 93 | if result and result.query then 94 | 95 | local query = result.query 96 | if query and query.normalized then 97 | text = query.normalized[1].to or text 98 | end 99 | 100 | local page = query.pages[next(query.pages)] 101 | 102 | if page and page.extract then 103 | return text..": "..page.extract 104 | else 105 | local text = "Extract not found for "..text 106 | text = text..'\n'..table.concat(wikiusage, '\n') 107 | return text 108 | end 109 | else 110 | return "Sorry an error happened" 111 | end 112 | end 113 | 114 | -- search for term in wiki 115 | function Wikipedia:wikisearch(text, lang) 116 | local result = self:loadPage(text, lang, true, true, true) 117 | 118 | if result and result.query then 119 | local titles = "" 120 | for i,item in pairs(result.query.search) do 121 | titles = titles .. "\n" .. item["title"] 122 | end 123 | titles = titles ~= "" and titles or "No results found" 124 | return titles 125 | else 126 | return "Sorry, an error occurred" 127 | end 128 | 129 | end 130 | 131 | local function run(msg, matches) 132 | -- TODO: Remember language (i18 on future version) 133 | -- TODO: Support for non Wikipedias but Mediawikis 134 | local search, term, lang 135 | if matches[1] == "search" then 136 | search = true 137 | term = matches[2] 138 | lang = nil 139 | elseif matches[2] == "search" then 140 | search = true 141 | term = matches[3] 142 | lang = matches[1] 143 | else 144 | term = matches[2] 145 | lang = matches[1] 146 | end 147 | if not term then 148 | term = lang 149 | lang = nil 150 | end 151 | if term == "" then 152 | local text = "Usage:\n" 153 | text = text..table.concat(wikiusage, '\n') 154 | return text 155 | end 156 | 157 | local result 158 | if search then 159 | result = Wikipedia:wikisearch(term, lang) 160 | else 161 | -- TODO: Show the link 162 | result = Wikipedia:wikintro(term, lang) 163 | end 164 | return result 165 | end 166 | 167 | return { 168 | description = "Searches Wikipedia and send results", 169 | usage = wikiusage, 170 | patterns = { 171 | "^![Ww]iki(%w+) (search) (.+)$", 172 | "^![Ww]iki (search) ?(.*)$", 173 | "^![Ww]iki(%w+) (.+)$", 174 | "^![Ww]iki ?(.*)$" 175 | }, 176 | run = run 177 | } 178 | -------------------------------------------------------------------------------- /plugins/xkcd.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | function get_last_id() 4 | local res,code = https.request("http://xkcd.com/info.0.json") 5 | if code ~= 200 then return "HTTP ERROR" end 6 | local data = json:decode(res) 7 | return data.num 8 | end 9 | 10 | function get_xkcd(id) 11 | local res,code = http.request("http://xkcd.com/"..id.."/info.0.json") 12 | if code ~= 200 then return "HTTP ERROR" end 13 | local data = json:decode(res) 14 | local link_image = data.img 15 | if link_image:sub(0,2) == '//' then 16 | link_image = msg.text:sub(3,-1) 17 | end 18 | return link_image, data.title, data.alt 19 | end 20 | 21 | 22 | function get_xkcd_random() 23 | local last = get_last_id() 24 | local i = math.random(1, last) 25 | return get_xkcd(i) 26 | end 27 | 28 | function send_title(cb_extra, success, result) 29 | if success then 30 | local message = cb_extra[2] .. "\n" .. cb_extra[3] 31 | send_msg(cb_extra[1], message, ok_cb, false) 32 | end 33 | end 34 | 35 | function run(msg, matches) 36 | local receiver = get_receiver(msg) 37 | if matches[1] == "!xkcd" then 38 | url, title, alt = get_xkcd_random() 39 | else 40 | url, title, alt = get_xkcd(matches[1]) 41 | end 42 | file_path = download_to_file(url) 43 | send_photo(receiver, file_path, send_title, {receiver, title, alt}) 44 | return false 45 | end 46 | 47 | return { 48 | description = "Send comic images from xkcd", 49 | usage = {"!xkcd (id): Send an xkcd image and title. If not id, send a random one"}, 50 | patterns = { 51 | "^!xkcd$", 52 | "^!xkcd (%d+)", 53 | "xkcd.com/(%d+)" 54 | }, 55 | run = run 56 | } 57 | 58 | end 59 | -------------------------------------------------------------------------------- /plugins/yoda.lua: -------------------------------------------------------------------------------- 1 | local ltn12 = require "ltn12" 2 | local https = require "ssl.https" 3 | 4 | -- Edit data/mashape.lua with your Mashape API key 5 | -- http://docs.mashape.com/api-keys 6 | local mashape = load_from_file('data/mashape.lua', { 7 | api_key = '' 8 | }) 9 | 10 | local function request(text) 11 | local api = "https://yoda.p.mashape.com/yoda?" 12 | text = string.gsub(text, " ", "+") 13 | local parameters = "sentence="..(text or "") 14 | local url = api..parameters 15 | 16 | local api_key = mashape.api_key 17 | if api_key:isempty() then 18 | return 'Configure your Mashape API Key' 19 | end 20 | 21 | local headers = { 22 | ["X-Mashape-Key"] = api_key, 23 | ["Accept"] = "text/plain" 24 | } 25 | 26 | local respbody = {} 27 | local body, code = https.request{ 28 | url = url, 29 | method = "GET", 30 | headers = headers, 31 | sink = ltn12.sink.table(respbody), 32 | protocol = "tlsv1" 33 | } 34 | if code ~= 200 then return code end 35 | local body = table.concat(respbody) 36 | return body 37 | end 38 | 39 | local function run(msg, matches) 40 | return request(matches[1]) 41 | end 42 | 43 | return { 44 | description = "Listen to Yoda and learn from his words!", 45 | usage = "!yoda You will learn how to speak like me someday.", 46 | patterns = { 47 | "^![y|Y]oda (.*)$" 48 | }, 49 | run = run 50 | } 51 | -------------------------------------------------------------------------------- /plugins/youtube.lua: -------------------------------------------------------------------------------- 1 | do 2 | local google_config = load_from_file('data/google.lua') 3 | 4 | local function httpsRequest(url) 5 | print(url) 6 | local res,code = https.request(url) 7 | if code ~= 200 then return nil end 8 | return json:decode(res) 9 | end 10 | 11 | function get_yt_data (yt_code) 12 | local url = 'https://www.googleapis.com/youtube/v3/videos?' 13 | url = url .. 'id=' .. URL.escape(yt_code) .. '&part=snippet' 14 | if google_config.api_keys then 15 | local i = math.random(#google_config.api_keys) 16 | local api_key = google_config.api_keys[i] 17 | if api_key then 18 | url = url.."&key="..api_key 19 | end 20 | end 21 | return httpsRequest(url) 22 | end 23 | 24 | function send_youtube_data(data, receiver) 25 | local title = data.title 26 | local description = data.description 27 | local uploader = data.channelTitle 28 | local text = title..' ('..uploader..')\n'..description 29 | local image_url = data.thumbnails.high.url or data.thumbnails.default.url 30 | local cb_extra = { 31 | receiver = receiver, 32 | url = image_url 33 | } 34 | send_msg(receiver, text, send_photo_from_url_callback, cb_extra) 35 | end 36 | 37 | function run(msg, matches) 38 | local yt_code = matches[1] 39 | local data = get_yt_data(yt_code) 40 | if data == nil or #data.items == 0 then 41 | return "I didn't find info about that video." 42 | end 43 | local senddata = data.items[1].snippet 44 | local receiver = get_receiver(msg) 45 | send_youtube_data(senddata, receiver) 46 | end 47 | 48 | return { 49 | description = "Sends YouTube info and image.", 50 | usage = "", 51 | patterns = { 52 | "youtu.be/([_A-Za-z0-9-]+)", 53 | "youtube.com/watch%?v=([_A-Za-z0-9-]+)", 54 | }, 55 | run = run 56 | } 57 | 58 | end 59 | --------------------------------------------------------------------------------