commit a03b1df203d6f797d72d341fd4304c67986240fe Author: micodev Date: Mon Mar 21 13:06:47 2016 +0300 By :- @malvoo diff --git a/README.md b/README.md new file mode 100644 index 0000000..1ba429b --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# chatter-bot + +A simple telegram-bot wtitten in LUA + +# commands +`/ban' + حظر عضو من ارسال رساله بالرد على رسالته +`/unban' +فتح الحظر عن عضو عن طريق الرد على رسالته +`/users` +معرفه عدد الاعضاء المشتركين +`/broadcast` +ارسل هذا الامر وكل رساله كانت بعده سترسل لجميع المشتركين +`/unbroadcast` +لكي تتوقف ارسال الرسائل وتفعيل الاوامر البقيه +`/start` +لأظهار رساله ترحيب للاعضاٱء +`/id` +بالرد على رساله موجهه يضهر لك المعلومات + +# التنصيب + + +انسخ الامر التالي + +``` +git clone https://github.com/micodev/chatter.git && cd chatter && chmod +x ./lua.sh && ./lua.sh run && ./lua.sh + +``` +خلي التوكين للبوت بين "" في bot_api_bot +وايدي حسابك الخاصه ب you + +```lua + +local bot_api_key = "" -- token +local BASE_URL = "https://api.telegram.org/bot"..bot_api_key +you = --ايدي هنا فقط رقم +local BASE_FOLDER = "" -- do not set this + +``` +اذا تحب تساعدني او عدك استفسار احب اسمع منك +@malvoo +او +@lua_lua + +لمعرفه جديد عن البوت 👍 + diff --git a/ban.db b/ban.db new file mode 100644 index 0000000..5899c11 --- /dev/null +++ b/ban.db @@ -0,0 +1 @@ +{"id":[]} \ No newline at end of file diff --git a/bot.lua b/bot.lua new file mode 100644 index 0000000..33b76b5 --- /dev/null +++ b/bot.lua @@ -0,0 +1,375 @@ +package.path = package.path .. ';.luarocks/share/lua/5.2/?.lua' + ..';.luarocks/share/lua/5.2/?/init.lua' +package.cpath = package.cpath .. ';.luarocks/lib/lua/5.2/?.so' +URL = require('socket.url') +JSON = require('dkjson') +HTTPS = require('ssl.https') +dofile('utilities.lua') +----config---- +local bot_api_key = "" +local You = --خلي ايدي حسابك +local BASE_URL = "https://api.telegram.org/bot"..bot_api_key +local BASE_FOLDER = "" +local start = [[ [مرحبا بك في بوت التواصل👀] + + + *{\TE_(SPΛCE)_CH/} * + [ +يمكنك طرح اي فكره او استفسار 💋 + + +او ماذا تريد ان اشرح لك خلال رساله + + +المطور :- @MALVOO +القناه :- @lua_lua] ]] + +------- + +----utilites---- + +function is_admin(msg)-- Check if user is admin or not + local var = false + local admins = {You} + for k,v in pairs(admins) do + if msg.from.id == v then + var = true + end + end + return var +end +function is_add(msg)-- Check if user is ADD or not + local var = false + local add = add.id + for k,v in pairs(add) do + if msg.from.id == v then + var = true + end + end + return var +end +function is_ban(msg)-- Check if user is ban or not + local var = false + local add = ban.id + for k,v in pairs(add) do + if msg.from.id == v then + var = true + end + end + return var +end +function sendRequest(url) + local dat, res = HTTPS.request(url) + local tab = JSON.decode(dat) + + if res ~= 200 then + return false, res + end + + if not tab.ok then + return false, tab.description + end + + return tab + +end +function getMe() + local url = BASE_URL .. '/getMe' + return sendRequest(url) +end +function getUpdates(offset) + + local url = BASE_URL .. '/getUpdates?timeout=20' + + if offset then + + url = url .. '&offset=' .. offset + + end + + return sendRequest(url) + +end +function sendSticker(chat_id, sticker, reply_to_message_id) + + local url = BASE_URL .. '/sendSticker' + + local curl_command = 'curl -s "' .. url .. '" -F "chat_id=' .. chat_id .. '" -F "sticker=@' .. sticker .. '"' + + if reply_to_message_id then + curl_command = curl_command .. ' -F "reply_to_message_id=' .. reply_to_message_id .. '"' + end + + io.popen(curl_command):read("*all") + return end +function sendPhoto(chat_id, photo, caption, reply_to_message_id) + + local url = BASE_URL .. '/sendPhoto' + + local curl_command = 'curl -s "' .. url .. '" -F "chat_id=' .. chat_id .. '" -F "photo=@' .. photo .. '"' + + if reply_to_message_id then + curl_command = curl_command .. ' -F "reply_to_message_id=' .. reply_to_message_id .. '"' + end + + if caption then + curl_command = curl_command .. ' -F "caption=' .. caption .. '"' + end + + io.popen(curl_command):read("*all") + return end +function forwardMessage(chat_id, from_chat_id, message_id) + + local url = BASE_URL .. '/forwardMessage?chat_id=' .. chat_id .. '&from_chat_id=' .. from_chat_id .. '&message_id=' .. message_id + + return sendRequest(url) + +end +function sendMessage(chat_id, text, disable_web_page_preview, reply_to_message_id, use_markdown) + + local url = BASE_URL .. '/sendMessage?chat_id=' .. chat_id .. '&text=' .. URL.escape(text) + + if disable_web_page_preview == true then + url = url .. '&disable_web_page_preview=true' + end + + if reply_to_message_id then + url = url .. '&reply_to_message_id=' .. reply_to_message_id + end + + if use_markdown then + url = url .. '&parse_mode=Markdown' + end + + return sendRequest(url) + +end +function sendDocument(chat_id, document, reply_to_message_id) + + local url = BASE_URL .. '/sendDocument' + + local curl_command = 'cd \''..BASE_FOLDER..currect_folder..'\' && curl -s "' .. url .. '" -F "chat_id=' .. chat_id .. '" -F "document=@' .. document .. '"' + + if reply_to_message_id then + curl_command = curl_command .. ' -F "reply_to_message_id=' .. reply_to_message_id .. '"' + end + io.popen(curl_command):read("*all") + return + +end +function download_to_file(url, file_name, file_path) + print("url to download: "..url) + + local respbody = {} + local options = { + url = url, + sink = ltn12.sink.table(respbody), + redirect = true + } + -- nil, code, headers, status + local response = nil + options.redirect = false + response = {HTTPS.request(options)} + local code = response[2] + local headers = response[3] + local status = response[4] + if code ~= 200 then return nil end + local file_path = BASE_FOLDER..currect_folder..file_name + + print("Saved to: "..file_path) + + file = io.open(file_path, "w+") + file:write(table.concat(respbody)) + file:close() + return file_path +end +function sendPhotoID(chat_id, file_id, caption, reply_to_message_id, disable_notification) + + local url = BASE_URL .. '/sendPhoto?chat_id=' .. chat_id .. '&photo=' .. file_id + + if caption then + url = url .. '&caption=' .. URL.escape(caption) + end + + if reply_to_message_id then + url = url .. '&reply_to_message_id=' .. reply_to_message_id + end + + if disable_notification then + url = url .. '&disable_notification=true' + end + + return sendRequest(url) + +end +function getUserProfilePhotos(user_id) +if user_id == nil then +return +else +local url = BASE_URL .. '/getUserProfilePhotos?user_id='..user_id +print(url) +return sendRequest(url) +end +end +function download_to_file(url, file_name, file_path) + print("url to download: "..url) + + local respbody = {} + local options = { + url = url, + sink = ltn12.sink.table(respbody), + redirect = true + } + -- nil, code, headers, status + local response = nil + options.redirect = false + response = {HTTPS.request(options)} + local code = response[2] + local headers = response[3] + local status = response[4] + if code ~= 200 then return nil end + local file_path = file_name + + print("Saved to: "..file_path) + + file = io.open(file_path, "w+") + file:write(table.concat(respbody)) + file:close() + return file_path +end +-------- @MALVOO & @DEV_MICO +function bot_run() + bot = nil + + while not bot do -- Get bot info + bot = getMe() + end + + bot = bot.result + if not add then + add = load_data('mico.db') + end + if not ban then + ban = load_data('ban.db') + end + local bot_info = "Username = @"..bot.username.."\nName = "..bot.first_name.."\nId = "..bot.id.." \nBASED BY :- @DEV_MICO , @MALVOO" + + print(bot_info) + for k,v in pairs(add.id) do + print(k.." :"..v) + end + + last_update = last_update or 0 + + currect_folder = "" + + is_running = true + math.randomseed(os.time()) + math.random() + + + last_cron = last_cron or os.date('%M', os.time()) -- the time of the last cron job, + is_started = true -- and whether or not the bot should be running. + add.id = add.id or {} --TABLE FUCKERRRRRRRRRRRRRRRRRRRRRRRRRRR + ban.id = ban.id or {} +end +function msg_processor(msg) +if is_ban(msg) then return end +if msg.date < os.time() - 5 then -- Ignore old msgs + return +end +if msg.text == "/unbroadcast" then + add.broadcast[msg.from.id] = "false" +end +if msg.text ~= "/broadcast" and add.broadcast[msg.from.id] == "true" then +if is_admin(msg) then +if msg.text and msg.text ~= "/unbroadcast" then +for k,v in pairs(add.id) do +sendMessage(v,string.gsub(msg.text,"/broadcast",""),true,false,true) +end + +elseif not msg.text then +for k,v in pairs(add.id) do +forwardMessage(v,You,msg.message_id) +end +end +end +else +if msg.text == "/start" and is_add(msg) then + print(#add.id) +sendMessage(msg.chat.id,start,true,false,true) +elseif is_admin(msg) and msg.text == "/users" then + local r = tostring(#add.id) + + local t = string.gsub(r," ","") +sendMessage(You,t,true,false,true) +elseif is_admin(msg) and msg.reply_to_message and msg.reply_to_message.forward_from ~= nil and msg.text == "/ban" then +table.insert(ban.id,msg.reply_to_message.forward_from.id) +sendMessage(msg.reply_to_message.forward_from.id,"*YOU HAVE BEEN BANNED*",true,false,true) +elseif is_admin(msg) and msg.reply_to_message and msg.reply_to_message.forward_from ~= nil and msg.text == "/unban" then +for k, v in pairs(ban["id"]) do +if ( v == msg.reply_to_message.forward_from.id ) then +table.remove(ban["id"], k) +end +end +sendMessage(msg.reply_to_message.forward_from.id,"*YOU HAVE BEEN unBANNED*",true,false,true) +elseif is_admin(msg) and msg.reply_to_message and msg.text == "/id" then + if msg.reply_to_message.forward_from.last_name ~= nil then + last_name = msg.reply_to_message.forward_from.last_name + else + last_name = "" + end + if msg.reply_to_message.forward_from.username ~= nil then + usernme = "\nUSERNAME : @"..msg.reply_to_message.forward_from.username + else + usernme = "" + end + local E = "NAME : "..msg.reply_to_message.forward_from.first_name.." "..last_name..usernme.."\nID :"..msg.reply_to_message.forward_from.id + sendMessage(msg.chat.id,E) + +elseif is_admin(msg) and msg.text == "/broadcast" then + if add.broadcast == nil then + add.broadcast = {} + add.broadcast[msg.from.id] = "true" + else + add.broadcast[msg.from.id] = "true" +end + +elseif msg.text == "/start" and not is_add(msg) then + table.insert(add.id,msg.from.id) + print("adding.....") +elseif not is_ban(msg) and msg.text ~= "/start" and msg.text ~= "/id" and msg.text ~= "/unban" and msg.text ~= "/ban" then +if is_admin(msg) and msg.reply_to_message then +forwardMessage(msg.reply_to_message.forward_from.id,msg.from.id,msg.message_id) +--print(msg.from.id) +print(msg.reply_to_message.from.id) +print(msg.reply_to_message.forward_from.id) +--print(msg.reply_to_message.from.id) +print(msg.reply_to_message.message_id) +elseif not is_admin(msg) then +forwardMessage(You,msg.chat.id,msg.message_id) +end +end +end +end +bot_run() -- Run main function +while is_running do -- Start a loop witch receive messages. + local response = getUpdates(last_update+1) -- Get the latest updates using getUpdates method + if response then + for i,v in ipairs(response.result) do + last_update = v.update_id + msg_processor(v.message) + end + else + print("Conection failed") + end +if last_cron ~= os.date('%M', os.time()) then -- Run cron jobs every minute. + last_cron = os.date('%M', os.time()) + save_data('mico.db', add) -- Save the database. + save_data('ban.db', ban) + + end +end +save_data('mico.db', add) +save_data('ban.db', ban) +print("Bot halted") diff --git a/dkjson.lua b/dkjson.lua new file mode 100644 index 0000000..8b3f37d --- /dev/null +++ b/dkjson.lua @@ -0,0 +1,809 @@ + -- Module options: + local always_try_using_lpeg = true + local register_global_module_table = false + local global_module_name = 'json' + + --[==[ + +David Kolf's JSON module for Lua 5.1/5.2 +======================================== +*Version 2.4* +In the default configuration this module writes no global values, not even +the module table. Import it using + json = require ("dkjson") +In environments where `require` or a similiar function are not available +and you cannot receive the return value of the module, you can set the +option `register_global_module_table` to `true`. The module table will +then be saved in the global variable with the name given by the option +`global_module_name`. +Exported functions and values: +`json.encode (object [, state])` +-------------------------------- +Create a string representing the object. `Object` can be a table, +a string, a number, a boolean, `nil`, `json.null` or any object with +a function `__tojson` in its metatable. A table can only use strings +and numbers as keys and its values have to be valid objects as +well. It raises an error for any invalid data types or reference +cycles. +`state` is an optional table with the following fields: + - `indent` + When `indent` (a boolean) is set, the created string will contain + newlines and indentations. Otherwise it will be one long line. + - `keyorder` + `keyorder` is an array to specify the ordering of keys in the + encoded output. If an object has keys which are not in this array + they are written after the sorted keys. + - `level` + This is the initial level of indentation used when `indent` is + set. For each level two spaces are added. When absent it is set + to 0. + - `buffer` + `buffer` is an array to store the strings for the result so they + can be concatenated at once. When it isn't given, the encode + function will create it temporary and will return the + concatenated result. + - `bufferlen` + When `bufferlen` is set, it has to be the index of the last + element of `buffer`. + - `tables` + `tables` is a set to detect reference cycles. It is created + temporary when absent. Every table that is currently processed + is used as key, the value is `true`. + +When `state.buffer` was set, the return value will be `true` on +success. Without `state.buffer` the return value will be a string. + +`json.decode (string [, position [, null]])` +-------------------------------------------- + +Decode `string` starting at `position` or at 1 if `position` was +omitted. + +`null` is an optional value to be returned for null values. The +default is `nil`, but you could set it to `json.null` or any other +value. + +The return values are the object or `nil`, the position of the next +character that doesn't belong to the object, and in case of errors +an error message. +Two metatables are created. Every array or object that is decoded gets +a metatable with the `__jsontype` field set to either `array` or +`object`. If you want to provide your own metatables use the syntax + json.decode (string, position, null, objectmeta, arraymeta) +To prevent the assigning of metatables pass `nil`: + json.decode (string, position, null, nil) +`.__jsonorder` +------------------------- +`__jsonorder` can overwrite the `keyorder` for a specific table. +`.__jsontype` +------------------------ +`__jsontype` can be either `"array"` or `"object"`. This value is only +checked for empty tables. (The default for empty tables is `"array"`). +`.__tojson (self, state)` +------------------------------------ +You can provide your own `__tojson` function in a metatable. In this +function you can either add directly to the buffer and return true, +or you can return a string. On errors nil and a message should be +returned. +`json.null` +----------- +You can use this value for setting explicit `null` values. +`json.version` +-------------- +Set to `"dkjson 2.4"`. +`json.quotestring (string)` +--------------------------- +Quote a UTF-8 string and escape critical characters using JSON +escape sequences. This function is only necessary when you build +your own `__tojson` functions. +`json.addnewline (state)` +------------------------- +When `state.indent` is set, add a newline to `state.buffer` and spaces +according to `state.level`. +LPeg support +------------ +When the local configuration variable `always_try_using_lpeg` is set, +this module tries to load LPeg to replace the `decode` function. The +speed increase is significant. You can get the LPeg module at + . +When LPeg couldn't be loaded, the pure Lua functions stay active. + +In case you don't want this module to require LPeg on its own, +disable the option `always_try_using_lpeg` in the options section at +the top of the module. +In this case you can later load LPeg support using +### `json.use_lpeg ()` +Require the LPeg module and replace the functions `quotestring` and +and `decode` with functions that use LPeg patterns. +This function returns the module table, so you can load the module +using: + json = require "dkjson".use_lpeg() +Alternatively you can use `pcall` so the JSON module still works when +LPeg isn't found. + + json = require "dkjson" + pcall (json.use_lpeg) + +### `json.using_lpeg` + +This variable is set to `true` when LPeg was loaded successfully. + +--------------------------------------------------------------------- + +Contact +------- + +You can contact the author by sending an e-mail to 'david' at the +domain 'dkolf.de'. + +--------------------------------------------------------------------- + +*Copyright (C) 2010-2013 David Heiko Kolf* + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + + \ No newline at end of file diff --git a/launch.sh b/launch.sh new file mode 100644 index 0000000..fe6c0af --- /dev/null +++ b/launch.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +THIS_DIR=$(cd $(dirname $0); pwd) +cd $THIS_DIR + +install_luarocks() { + git clone https://github.com/keplerproject/luarocks.git + cd luarocks + git checkout tags/v2.2.1 # Current stable + + PREFIX="$THIS_DIR/.luarocks" + + ./configure --prefix=$PREFIX --sysconfdir=$PREFIX/luarocks --force-config + + RET=$?; if [ $RET -ne 0 ]; + then echo "Error. Exiting."; exit $RET; + fi + + make build && make install + RET=$?; if [ $RET -ne 0 ]; + then echo "Error. Exiting.";exit $RET; + fi + + cd .. + rm -rf luarocks +} + +install_rocks() { + + ./.luarocks/bin/luarocks install luasec + RET=$?; if [ $RET -ne 0 ]; + then echo "Error. Exiting."; exit $RET; + fi + + ./.luarocks/bin/luarocks install luasocket + RET=$?; if [ $RET -ne 0 ]; + then echo "Error. Exiting."; exit $RET; + fi + +} + +if [ "$1" = "install" ]; then + install_luarocks + install_rocks +else + while true; do + lua bot.lua + sleep 5s + done +fi diff --git a/lua.sh b/lua.sh new file mode 100644 index 0000000..733abc5 --- /dev/null +++ b/lua.sh @@ -0,0 +1 @@ +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 && sudo apt-get install lua-socket && sudo apt-get install lua-sec \ No newline at end of file diff --git a/mico.db b/mico.db new file mode 100644 index 0000000..5899c11 --- /dev/null +++ b/mico.db @@ -0,0 +1 @@ +{"id":[]} \ No newline at end of file diff --git a/utilities.lua b/utilities.lua new file mode 100644 index 0000000..c744b13 --- /dev/null +++ b/utilities.lua @@ -0,0 +1,198 @@ +-- utilities.lua +-- Functions shared among plugins. + + -- you're welcome, brayden :^) +HTTP = HTTP or require('socket.http') +HTTPS = HTTPS or require('ssl.https') +JSON = JSON or require('cjson') + + -- get the indexed word in a string +get_word = function(s, i) + + s = s or '' + i = i or 1 + + local t = {} + for w in s:gmatch('%g+') do + table.insert(t, w) + end + + return t[i] or false + +end + + -- Returns the string after the first space. +function string:input() + if not self:find(' ') then + return false + end + return self:sub(self:find(' ')+1) +end + + -- I swear, I copied this from PIL, not yago! :) +function string:trim() -- Trims whitespace from a string. + local s = self:gsub('^%s*(.-)%s*$', '%1') + return s +end + +local lc_list = { +-- Latin = 'Cyrillic' + ['A'] = 'А', + ['B'] = 'В', + ['C'] = 'С', + ['E'] = 'Е', + ['I'] = 'І', + ['J'] = 'Ј', + ['K'] = 'К', + ['M'] = 'М', + ['H'] = 'Н', + ['O'] = 'О', + ['P'] = 'Р', + ['S'] = 'Ѕ', + ['T'] = 'Т', + ['X'] = 'Х', + ['Y'] = 'Ү', + ['a'] = 'а', + ['c'] = 'с', + ['e'] = 'е', + ['i'] = 'і', + ['j'] = 'ј', + ['o'] = 'о', + ['s'] = 'ѕ', + ['x'] = 'х', + ['y'] = 'у', + ['!'] = 'ǃ' +} + + -- Replaces letters with corresponding Cyrillic characters. +latcyr = function(str) + for k,v in pairs(lc_list) do + str = string.gsub(str, k, v) + end + return str +end + + -- Loads a JSON file as a table. +load_data = function(filename) + + local f = io.open(filename) + if not f then + return {} + end + local s = f:read('*all') + f:close() + local data = JSON.decode(s) + + return data + +end + + -- Saves a table to a JSON file. +save_data = function(filename, data) + + local s = JSON.encode(data) + local f = io.open(filename, 'w') + f:write(s) + f:close() + +end + + -- Gets coordinates for a location. Used by gMaps.lua, time.lua, weather.lua. +get_coords = function(input) + + local url = 'http://maps.googleapis.com/maps/api/geocode/json?address=' .. URL.escape(input) + + local jstr, res = HTTP.request(url) + if res ~= 200 then + return config.errors.connection + end + + local jdat = JSON.decode(jstr) + if jdat.status == 'ZERO_RESULTS' then + return config.errors.results + end + + return { + lat = jdat.results[1].geometry.location.lat, + lon = jdat.results[1].geometry.location.lng + } + +end + + -- Get the number of values in a key/value table. +table_size = function(tab) + + local i = 0 + for k,v in pairs(tab) do + i = i + 1 + end + return i + +end + +resolve_username = function(target) + -- If $target is a known username, returns associated ID. + -- If $target is an unknown username, returns nil. + -- If $target is a number, returns that number. + -- Otherwise, returns false. + + local input = tostring(target):lower() + if input:match('^@') then + local uname = input:gsub('^@', '') + return add.usernames[uname] + else + return tonumber(target) or false + end + +end + +handle_exception = function(err, message) + + if not err then err = '' end + + local output = '\n[' .. os.date('%F %T', os.time()) .. ']\n' .. bot.username .. ': ' .. err .. '\n' .. message .. '\n' + + if config.log_chat then + output = '```' .. output .. '```' + sendMessage(config.log_chat, output, true, nil, true) + else + print(output) + end + +end + + -- Okay, this one I actually did copy from yagop. + -- https://github.com/yagop/telegram-bot/blob/master/bot/utils.lua +download_file = function(url, filename) + + local respbody = {} + local options = { + url = url, + sink = ltn12.sink.table(respbody), + redirect = true + } + + local response = nil + + if url:match('^https') then + options.redirect = false + response = { HTTPS.request(options) } + else + response = { HTTP.request(options) } + end + + local code = response[2] + local headers = response[3] + local status = response[4] + + if code ~= 200 then return false end + + filename = filename or '/tmp/' .. os.time() + + local file = io.open(filename, 'w+') + file:write(table.concat(respbody)) + file:close() + + return filename + +end