Merge pull request #1 from Brawl345/master
Übernehme änderungen von Brawlbot
This commit is contained in:
commit
5c75429586
35
.travis.yml
Normal file
35
.travis.yml
Normal file
@ -0,0 +1,35 @@
|
||||
language: erlang
|
||||
|
||||
before_install:
|
||||
- CURDIR=$(pwd)
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev make unzip git libjansson-dev
|
||||
- THIS_DIR=$(cd $(dirname $0); pwd)
|
||||
- cd $THIS_DIR
|
||||
- git clone https://github.com/keplerproject/luarocks.git
|
||||
- cd luarocks
|
||||
- git checkout tags/v2.3.0
|
||||
- PREFIX="$THIS_DIR/.luarocks"
|
||||
- ./configure --prefix=$PREFIX --sysconfdir=$PREFIX/luarocks --force-config
|
||||
- make build && make install
|
||||
- cd ..
|
||||
- rm -rf luarocks
|
||||
- ./.luarocks/bin/luarocks install luasocket
|
||||
- ./.luarocks/bin/luarocks install luasec
|
||||
- ./.luarocks/bin/luarocks install multipart-post
|
||||
- ./.luarocks/bin/luarocks install dkjson
|
||||
- ./.luarocks/bin/luarocks install oauth
|
||||
- ./.luarocks/bin/luarocks install redis-lua
|
||||
- ./.luarocks/bin/luarocks install lua-cjson
|
||||
- ./.luarocks/bin/luarocks install fakeredis
|
||||
- ./.luarocks/bin/luarocks install xml
|
||||
- ./.luarocks/bin/luarocks install feedparser
|
||||
- ./.luarocks/bin/luarocks install serpent
|
||||
- ./.luarocks/bin/luarocks install sha1
|
||||
- ./.luarocks/bin/luarocks install lpeg
|
||||
- cd $CURDIR
|
||||
|
||||
|
||||
script:
|
||||
- luac -p otouto/*.lua
|
||||
- luac -p otouto/plugins/*.lua
|
31
README.md
31
README.md
@ -1,4 +1,6 @@
|
||||
# Brawlbot v2
|
||||
[![Build Status](https://travis-ci.org/Brawl345/Brawlbot-v2.svg?branch=master)](https://travis-ci.org/Brawl345/Brawlbot-v2)
|
||||
|
||||
The plugin-wielding, multipurpose Telegram bot.
|
||||
|
||||
[Public Bot](http://telegram.me/mokubot) | [Official Channel](http://telegram.me/otouto) | [Development Group](http://telegram.me/BotDevelopment)
|
||||
@ -11,11 +13,10 @@ otouto is free software; you are free to redistribute it and/or modify it under
|
||||
|
||||
| For Users | For Coders |
|
||||
|:----------------------------------------------|:------------------------------|
|
||||
| [Setup](#setup) | [Introduction](#introduction) |
|
||||
| [Control plugins](#control-plugins) | [Plugins](#plugins) |
|
||||
| [Group Administration](#group-administration) | [Bindings](#bindings) |
|
||||
| [List of plugins](#list-of-plugins) | [Output style](#output-style) |
|
||||
| | [Contributors](#contributors) |
|
||||
| [Setup](#setup) | [Plugins](#plugins) |
|
||||
| [Control plugins](#control-plugins) | [Bindings](#bindings) |
|
||||
| [Group Administration](#group-administration) | [Output style](#output-style) |
|
||||
| [List of plugins](#list-of-plugins) | [Contributors](#contributors) |
|
||||
|
||||
## Setup
|
||||
You _must_ have Lua (5.2+), luasocket, luasec, multipart-post, and dkjson installed. You should also have lpeg, though it is not required. It is recommended you install these with LuaRocks.
|
||||
@ -194,9 +195,6 @@ Additionally, antiflood can be configured to automatically ban a user after he h
|
||||
|
||||
* * *
|
||||
|
||||
## Introduction
|
||||
####todo
|
||||
|
||||
## Plugins
|
||||
otouto uses a robust plugin system, similar to yagop's [Telegram-Bot](http://github.com/yagop/telegram-bot).
|
||||
|
||||
@ -204,14 +202,15 @@ Most plugins are intended for public use, but a few are for other purposes, like
|
||||
|
||||
A plugin can have five components, and two of them are required:
|
||||
|
||||
| Component | Description | Required? |
|
||||
|:----------------|:---------------------------------------------|:----------|
|
||||
| plugin:action | Main function. Expects `msg` table as an argument. | Y |
|
||||
| plugin.triggers | Table of triggers for the plugin. Uses Lua patterns. | Y |
|
||||
| plugin:init | Optional function run when the plugin is loaded. | N |
|
||||
| plugin:cron | Optional function to be called every minute. | N |
|
||||
| plugin.command | Basic command and syntax. Listed in the help text. | N |
|
||||
| plugin.doc | Usage for the plugin. Returned by "/help $command". | N |
|
||||
| Component | Description | Required? |
|
||||
|:------------------|:---------------------------------------------|:----------|
|
||||
| `plugin:action` | Main function. Expects `msg` table as an argument. | Y |
|
||||
| `plugin.triggers` | Table of triggers for the plugin. Uses Lua patterns. | Y |
|
||||
| `plugin:init` | Optional function run when the plugin is loaded. | N |
|
||||
| `plugin:cron` | Optional function to be called every minute. | N |
|
||||
| `plugin.command` | Basic command and syntax. Listed in the help text. | N |
|
||||
| `plugin.doc` | Usage for the plugin. Returned by "/help $command". | N |
|
||||
| `plugin.error` | Plugin-specific error message; false for no message. | N |
|
||||
|
||||
The `bot:on_msg_receive` function adds a few variables to the `msg` table for your convenience. These are self-explanatory: `msg.from.id_str`, `msg.to.id_str`, `msg.chat.id_str`, `msg.text_lower`, `msg.from.name`.
|
||||
|
||||
|
@ -22,6 +22,7 @@ Sende /hilfe, um zu starten
|
||||
cmd_pat = '/',
|
||||
|
||||
errors = { -- Generic error messages used in various plugins.
|
||||
generic = 'An unexpected error occurred.',
|
||||
connection = 'Verbindungsfehler.',
|
||||
quotaexceeded = 'API-Quota aufgebraucht.',
|
||||
results = 'Keine Ergebnisse gefunden.',
|
||||
@ -30,7 +31,7 @@ Sende /hilfe, um zu starten
|
||||
syntax = 'Invalide Syntax.',
|
||||
chatter_connection = 'Ich möchte gerade nicht reden',
|
||||
chatter_response = 'Ich weiß nicht, was ich darauf antworten soll.'
|
||||
},
|
||||
}
|
||||
|
||||
plugins = { -- To enable a plugin, add its name to the list.
|
||||
'control',
|
||||
|
@ -1,22 +0,0 @@
|
||||
package = 'otouto'
|
||||
version = 'dev-1'
|
||||
|
||||
source = {
|
||||
url = 'git://github.com/topkecleon/otouto.git'
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = 'The plugin-wielding, multipurpose Telegram bot!',
|
||||
detailed = 'A plugin-wielding, multipurpose bot for the Telegram API.',
|
||||
homepage = 'http://otou.to',
|
||||
maintainer = 'Drew <drew@otou.to>',
|
||||
license = 'GPL-2'
|
||||
}
|
||||
|
||||
dependencies = {
|
||||
'lua >= 5.2',
|
||||
'LuaSocket ~> 3.0',
|
||||
'LuaSec ~> 0.6',
|
||||
'dkjson ~> 2.5',
|
||||
'LPeg ~> 1.0'
|
||||
}
|
@ -5,7 +5,7 @@ local bindings -- Load Telegram bindings.
|
||||
local utilities -- Load miscellaneous and cross-plugin functions.
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
bot.version = '2'
|
||||
bot.version = '2.0'
|
||||
|
||||
function bot:init(config) -- The function run when the bot is started or reloaded.
|
||||
|
||||
@ -69,14 +69,28 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec
|
||||
msg.text_lower = msg.text:lower()
|
||||
end
|
||||
|
||||
for _,v in ipairs(self.plugins) do
|
||||
for _,w in pairs(v.triggers) do
|
||||
if string.match(msg.text_lower, w) then
|
||||
for _, plugin in ipairs(self.plugins) do
|
||||
for _, trigger in pairs(plugin.triggers) do
|
||||
if string.match(msg.text_lower, trigger) then
|
||||
local success, result = pcall(function()
|
||||
return v.action(self, msg, config)
|
||||
-- trying to port matches to otouto
|
||||
for k, pattern in pairs(plugin.triggers) do
|
||||
matches = match_pattern(pattern, msg.text)
|
||||
if matches then
|
||||
break;
|
||||
end
|
||||
end
|
||||
return plugin.action(self, msg, config, matches)
|
||||
end)
|
||||
if not success then
|
||||
utilities.send_reply(self, msg, 'Sorry, an unexpected error occurred.')
|
||||
-- If the plugin has an error message, send it. If it does
|
||||
-- not, use the generic one specified in config. If it's set
|
||||
-- to false, do nothing.
|
||||
if plugin.error then
|
||||
utilities.send_reply(self, msg, plugin.error)
|
||||
elseif plugin.error == nil then
|
||||
utilities.send_reply(self, msg, config.errors.generic, true)
|
||||
end
|
||||
utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config)
|
||||
return
|
||||
end
|
||||
|
@ -26,6 +26,7 @@ function ninegag:get_9GAG()
|
||||
end
|
||||
|
||||
function ninegag:action(msg, config)
|
||||
utilities.send_typing(self, msg.chat.id, 'upload_photo')
|
||||
local url, title = ninegag:get_9GAG()
|
||||
if not url then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
@ -33,9 +34,7 @@ function ninegag:action(msg, config)
|
||||
end
|
||||
|
||||
local file = download_to_file(url)
|
||||
bindings.sendPhoto(self, {chat_id = msg.chat.id, caption = title}, {photo = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
utilities.send_photo(self, msg.chat.id, file, title)
|
||||
end
|
||||
|
||||
return ninegag
|
||||
|
@ -71,6 +71,9 @@ function administration:init(config)
|
||||
|
||||
administration.doc = '`Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.`'
|
||||
|
||||
-- In the worst case, don't send errors in reply to random messages.
|
||||
administration.error = false
|
||||
|
||||
end
|
||||
|
||||
function administration.init_flags(cmd_pat) return {
|
||||
@ -263,9 +266,9 @@ function administration:kick_user(chat, target, reason, config)
|
||||
local victim = target
|
||||
if self.database.users[tostring(target)] then
|
||||
victim = utilities.build_name(
|
||||
self.database.users[tostring(target)].first_name,
|
||||
self.database.users[tostring(target)].last_name
|
||||
)
|
||||
self.database.users[tostring(target)].first_name,
|
||||
self.database.users[tostring(target)].last_name
|
||||
) .. ' [' .. victim .. ']'
|
||||
end
|
||||
local group = self.database.administration.groups[tostring(chat)].name
|
||||
utilities.handle_exception(self, victim..' kicked from '..group, reason, config)
|
||||
@ -917,7 +920,9 @@ function administration.init_command(self_, config)
|
||||
if input then
|
||||
input = utilities.get_word(input, 1)
|
||||
input = tonumber(input)
|
||||
if not input or not administration.flags[input] then input = false end
|
||||
if not input or not administration.flags[input] then
|
||||
input = false
|
||||
end
|
||||
end
|
||||
if not input then
|
||||
local output = '*Flags for ' .. msg.chat.title .. ':*\n'
|
||||
@ -1209,7 +1214,9 @@ function administration.init_command(self_, config)
|
||||
doc = 'Adds a group to the administration system. Pass numbers as arguments to enable those flags immediately. For example, this would add the group and enable the unlisted flag, antibot, and antiflood:\n/gadd 1 4 5',
|
||||
|
||||
action = function(self, msg, group, config)
|
||||
if self.database.administration.groups[msg.chat.id_str] then
|
||||
if msg.chat.id == msg.from.id then
|
||||
utilities.send_message(self, msg.chat.id, 'No.')
|
||||
elseif self.database.administration.groups[msg.chat.id_str] then
|
||||
utilities.send_reply(self, msg, 'I am already administrating this group.')
|
||||
else
|
||||
local flags = {}
|
||||
@ -1327,6 +1334,18 @@ function administration.init_command(self_, config)
|
||||
end
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
{ -- /buildwall :^)
|
||||
triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('buildwall').table,
|
||||
privilege = 3,
|
||||
interior = true,
|
||||
action = function(self, msg, group, config)
|
||||
for i = 2, 5 do
|
||||
group.flags[i] = true
|
||||
end
|
||||
utilities.send_message(self, msg.chat.id, 'antisquig, antisquig++, antibot, and antiflood have been enabled.')
|
||||
end
|
||||
}
|
||||
|
||||
}
|
||||
|
117
otouto/plugins/app_store.lua
Normal file
117
otouto/plugins/app_store.lua
Normal file
@ -0,0 +1,117 @@
|
||||
local app_store = {}
|
||||
|
||||
local https = require('ssl.https')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
app_store.triggers = {
|
||||
"itunes.apple.com/(.*)/app/(.*)/id(%d+)",
|
||||
"^!itunes (%d+)$",
|
||||
"itunes.apple.com/app/id(%d+)"
|
||||
}
|
||||
|
||||
local BASE_URL = 'https://itunes.apple.com/lookup'
|
||||
|
||||
local makeOurDate = function(dateString)
|
||||
local pattern = "(%d+)%-(%d+)%-(%d+)T"
|
||||
local year, month, day = dateString:match(pattern)
|
||||
return day..'.'..month..'.'..year
|
||||
end
|
||||
|
||||
function app_store:get_appstore_data()
|
||||
local url = BASE_URL..'/?id='..appid..'&country=de'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return "HTTP-FEHLER" end
|
||||
local data = json.decode(res).results[1]
|
||||
|
||||
if data == nil then return 'NOTFOUND' end
|
||||
if data.wrapperType ~= 'software' then return nil end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
function app_store:send_appstore_data(data)
|
||||
-- Header
|
||||
local name = data.trackName
|
||||
local author = data.sellerName
|
||||
local price = data.formattedPrice
|
||||
local version = data.version
|
||||
|
||||
-- Body
|
||||
local description = string.sub(unescape(data.description), 1, 150) .. '...'
|
||||
local min_ios_ver = data.minimumOsVersion
|
||||
local size = string.gsub(round(data.fileSizeBytes / 1000000, 2), "%.", ",") -- wtf Apple, it's 1024, not 1000!
|
||||
local release = makeOurDate(data.releaseDate)
|
||||
if data.isGameCenterEnabled then
|
||||
game_center = '\nUnterstützt Game Center'
|
||||
else
|
||||
game_center = ''
|
||||
end
|
||||
local category_count = tablelength(data.genres)
|
||||
if category_count == 1 then
|
||||
category = '\nKategorie: '..data.genres[1]
|
||||
else
|
||||
local category_loop = '\nKategorien: '
|
||||
for v in pairs(data.genres) do
|
||||
if v < category_count then
|
||||
category_loop = category_loop..data.genres[v]..', '
|
||||
else
|
||||
category_loop = category_loop..data.genres[v]
|
||||
end
|
||||
end
|
||||
category = category_loop
|
||||
end
|
||||
|
||||
-- Footer
|
||||
if data.averageUserRating and data.userRatingCount then
|
||||
avg_rating = 'Bewertung: '..string.gsub(data.averageUserRating, "%.", ",")..' Sterne '
|
||||
ratings = 'von '..comma_value(data.userRatingCount)..' Bewertungen'
|
||||
else
|
||||
avg_rating = ""
|
||||
ratings = ""
|
||||
end
|
||||
|
||||
|
||||
local header = '*'..name..'* v'..version..' von *'..author..'* ('..price..'):'
|
||||
local body = '\n'..description..'\n_Benötigt mind. iOS '..min_ios_ver..'_\nGröße: '..size..' MB\nErstveröffentlicht am '..release..game_center..category
|
||||
local footer = '\n'..avg_rating..ratings
|
||||
local text = header..body..footer
|
||||
|
||||
-- Picture
|
||||
if data.screenshotUrls[1] and data.ipadScreenshotUrls[1] then
|
||||
image_url = data.screenshotUrls[1]
|
||||
elseif data.screenshotUrls[1] and not data.ipadScreenshotUrls[1] then
|
||||
image_url = data.screenshotUrls[1]
|
||||
elseif not data.screenshotUrls[1] and data.ipadScreenshotUrls[1] then
|
||||
image_url = data.ipadScreenshotUrls[1]
|
||||
else
|
||||
image_url = nil
|
||||
end
|
||||
|
||||
return text, image_url
|
||||
end
|
||||
|
||||
function app_store:action(msg, config, matches)
|
||||
if not matches[3] then
|
||||
appid = matches[1]
|
||||
else
|
||||
appid = matches[3]
|
||||
end
|
||||
local data = app_store:get_appstore_data()
|
||||
if data == nil then print('Das Appstore-Plugin unterstützt nur Apps!') end
|
||||
if data == 'HTTP-FEHLER' or data == 'NOTFOUND' then
|
||||
utilities.send_reply(self, msg, '*App nicht gefunden!*', true)
|
||||
return
|
||||
else
|
||||
local output, image_url = app_store:send_appstore_data(data)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
if image_url then
|
||||
utilities.send_typing(self, msg.chat.id, 'upload_photo')
|
||||
local file = download_to_file(image_url)
|
||||
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return app_store
|
48
otouto/plugins/bitly.lua
Normal file
48
otouto/plugins/bitly.lua
Normal file
@ -0,0 +1,48 @@
|
||||
local bitly = {}
|
||||
|
||||
local https = require('ssl.https')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function bitly:init(config)
|
||||
if not cred_data.bitly_access_token then
|
||||
print('Missing config value: bitly_access_token.')
|
||||
print('bitly.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
bitly.triggers = {
|
||||
"bit.ly/([A-Za-z0-9-_-]+)",
|
||||
"bitly.com/([A-Za-z0-9-_-]+)",
|
||||
"j.mp/([A-Za-z0-9-_-]+)",
|
||||
"andib.tk/([A-Za-z0-9-_-]+)"
|
||||
}
|
||||
end
|
||||
|
||||
local BASE_URL = 'https://api-ssl.bitly.com/v3/expand'
|
||||
|
||||
function bitly:expand_bitly_link (shorturl)
|
||||
local access_token = cred_data.bitly_access_token
|
||||
local url = BASE_URL..'?access_token='..access_token..'&shortUrl=https://bit.ly/'..shorturl
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return "HTTP-FEHLER" end
|
||||
local data = json.decode(res).data.expand[1]
|
||||
cache_data('bitly', shorturl, data)
|
||||
return data.long_url
|
||||
end
|
||||
|
||||
function bitly:action(msg, config, matches)
|
||||
local shorturl = matches[1]
|
||||
local hash = 'telegram:cache:bitly:'..shorturl
|
||||
if redis:exists(hash) == false then
|
||||
utilities.send_reply(self, msg, bitly:expand_bitly_link(shorturl))
|
||||
return
|
||||
else
|
||||
local data = redis:hgetall(hash)
|
||||
utilities.send_reply(self, msg, data.long_url)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return bitly
|
142
otouto/plugins/bitly_create.lua
Normal file
142
otouto/plugins/bitly_create.lua
Normal file
@ -0,0 +1,142 @@
|
||||
local bitly_create = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local OAuth = require "OAuth"
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function bitly_create:init(config)
|
||||
if not cred_data.bitly_client_id then
|
||||
print('Missing config value: bitly_client_id.')
|
||||
print('bitly_create.lua will not be enabled.')
|
||||
return
|
||||
elseif not cred_data.bitly_client_secret then
|
||||
print('Missing config value: bitly_client_secret.')
|
||||
print('bitly_create.lua will not be enabled.')
|
||||
return
|
||||
elseif not cred_data.bitly_redirect_uri then
|
||||
print('Missing config value: bitly_redirect_uri.')
|
||||
print('bitly_create.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
bitly_create.triggers = {
|
||||
"^/short (auth) (.+)$",
|
||||
"^/short (auth)$",
|
||||
"^/short (unauth)$",
|
||||
"^/short (me)$",
|
||||
"^/short (j.mp) (https?://[%w-_%.%?%.:/%+=&]+)$",
|
||||
"^/short (bit.ly) (https?://[%w-_%.%?%.:/%+=&]+)$",
|
||||
"^/short (bitly.com) (https?://[%w-_%.%?%.:/%+=&]+)$",
|
||||
"^/short (https?://[%w-_%.%?%.:/%+=&]+)$"
|
||||
}
|
||||
bitly_create.doc = [[*
|
||||
]]..config.cmd_pat..[[short* _<Link>_: Kürzt einen Link mit der Standard Bitly-Adresse
|
||||
*]]..config.cmd_pat..[[short* _<j.mp|bit.ly|bitly.com>_ _[Link]_: Kürzt einen Link mit der ausgewählten Kurz-URL
|
||||
*]]..config.cmd_pat..[[short* _auth_: Loggt deinen Account ein und nutzt ihn für deine Links (empfohlen!)
|
||||
*]]..config.cmd_pat..[[short* _me_: Gibt den eingeloggten Account aus
|
||||
*]]..config.cmd_pat..[[short* _unauth_: Loggt deinen Account aus
|
||||
]]
|
||||
end
|
||||
|
||||
bitly_create.command = 'short <URL>'
|
||||
|
||||
local BASE_URL = 'https://api-ssl.bitly.com'
|
||||
|
||||
local client_id = cred_data.bitly_client_id
|
||||
local client_secret = cred_data.bitly_client_secret
|
||||
local redirect_uri = cred_data.bitly_redirect_uri
|
||||
|
||||
function bitly_create:get_bitly_access_token(hash, code)
|
||||
local req = post_petition(BASE_URL..'/oauth/access_token', 'client_id='..client_id..'&client_secret='..client_secret..'&code='..code..'&redirect_uri='..redirect_uri)
|
||||
if not req.access_token then return '*Fehler beim Einloggen!*' end
|
||||
|
||||
local access_token = req.access_token
|
||||
local login_name = req.login
|
||||
redis:hset(hash, 'bitly', access_token)
|
||||
return 'Erfolgreich als `'..login_name..'` eingeloggt!'
|
||||
end
|
||||
|
||||
function bitly_create:get_bitly_user_info(bitly_access_token)
|
||||
local url = BASE_URL..'/v3/user/info?access_token='..bitly_access_token..'&format=json'
|
||||
local res,code = https.request(url)
|
||||
if code == 401 then return 'Login fehlgeschlagen!' end
|
||||
if code ~= 200 then return 'HTTP-Fehler!' end
|
||||
|
||||
local data = json.decode(res).data
|
||||
|
||||
if data.full_name then
|
||||
name = '*'..data.full_name..'* (`'..data.login..'`)'
|
||||
else
|
||||
name = '`'..data.login..'`'
|
||||
end
|
||||
|
||||
local text = 'Eingeloggt als '..name
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
function bitly_create:create_bitlink (long_url, domain, bitly_access_atoken)
|
||||
local url = BASE_URL..'/v3/shorten?access_token='..bitly_access_token..'&domain='..domain..'&longUrl='..long_url..'&format=txt'
|
||||
local text,code = https.request(url)
|
||||
if code ~= 200 then return 'FEHLER: '..text end
|
||||
return text
|
||||
end
|
||||
|
||||
function bitly_create:action(msg, config, matches)
|
||||
local hash = 'user:'..msg.from.id
|
||||
bitly_access_token = redis:hget(hash, 'bitly')
|
||||
|
||||
if matches[1] == 'auth' and matches[2] then
|
||||
utilities.send_reply(self, msg, bitly_create:get_bitly_access_token(hash, matches[2]), true)
|
||||
return
|
||||
end
|
||||
|
||||
if matches[1] == 'auth' then
|
||||
utilities.send_reply(self, msg, 'Bitte logge dich ein und folge den Anweisungen:\n[Bei Bitly anmelden](https://bitly.com/oauth/authorize?client_id='..client_id..'&redirect_uri='..redirect_uri..')', true)
|
||||
return
|
||||
end
|
||||
|
||||
if matches[1] == 'unauth' and bitly_access_token then
|
||||
redis:hdel(hash, 'bitly')
|
||||
utilities.send_reply(self, msg, '*Erfolgreich ausgeloggt!* Du kannst den Zugriff [in deinen Kontoeinstellungen](https://bitly.com/a/settings/connected) endgültig entziehen.', true)
|
||||
return
|
||||
elseif matches[1] == 'unauth' and not bitly_access_token then
|
||||
utilities.send_reply(self, msg, 'Wie willst du dich ausloggen, wenn du gar nicht eingeloggt bist?', true)
|
||||
return
|
||||
end
|
||||
|
||||
if matches[1] == 'me' and bitly_access_token then
|
||||
local text = bitly_create:get_bitly_user_info(bitly_access_token)
|
||||
if text then
|
||||
utilities.send_reply(self, msg, text, true)
|
||||
return
|
||||
else
|
||||
return
|
||||
end
|
||||
elseif matches[1] == 'me' and not bitly_access_token then
|
||||
utilities.send_reply(self, msg, 'Du bist nicht eingeloggt! Logge dich ein mit\n/short auth', true)
|
||||
return
|
||||
end
|
||||
|
||||
if not bitly_access_token then
|
||||
print('Not signed in, will use global bitly access_token')
|
||||
bitly_access_token = cred_data.bitly_access_token
|
||||
end
|
||||
|
||||
if matches[2] == nil then
|
||||
long_url = url_encode(matches[1])
|
||||
domain = 'bit.ly'
|
||||
else
|
||||
long_url = url_encode(matches[2])
|
||||
domain = matches[1]
|
||||
end
|
||||
utilities.send_reply(self, msg, bitly_create:create_bitlink(long_url, domain, bitly_access_token))
|
||||
return
|
||||
end
|
||||
|
||||
return bitly_create
|
125
otouto/plugins/creds.lua
Normal file
125
otouto/plugins/creds.lua
Normal file
@ -0,0 +1,125 @@
|
||||
local creds_manager = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function creds_manager:init(config)
|
||||
creds_manager.triggers = {
|
||||
"^(/creds)$",
|
||||
"^(/creds add) ([^%s]+) (.+)$",
|
||||
"^(/creds del) (.+)$",
|
||||
"^(/creds rename) ([^%s]+) (.+)$"
|
||||
}
|
||||
creds_manager.doc = [[*
|
||||
]]..config.cmd_pat..[[creds*: Zeigt alle Logindaten und API-Keys
|
||||
*]]..config.cmd_pat..[[creds* _add_ _<Variable>_ _<Schlüssel>_: Speichert Schlüssel mit dieser Variable ein
|
||||
*]]..config.cmd_pat..[[creds* _del_ _<Variable>_: Löscht Schlüssel mit dieser Variable
|
||||
*]]..config.cmd_pat..[[creds* _rename_ _<Variable>_ _<Neue Variable>_: Benennt Variable um, behält Schlüssel bei
|
||||
]]
|
||||
end
|
||||
|
||||
creds_manager.command = 'creds'
|
||||
|
||||
local hash = "telegram:credentials"
|
||||
|
||||
-- See: http://www.lua.org/pil/19.3.html
|
||||
function pairsByKeys (t, f)
|
||||
local a = {}
|
||||
for n in pairs(t) do table.insert(a, n) end
|
||||
table.sort(a, f)
|
||||
local i = 0 -- iterator variable
|
||||
local iter = function () -- iterator function
|
||||
i = i + 1
|
||||
if a[i] == nil then
|
||||
return nil
|
||||
else
|
||||
return a[i], t[a[i]]
|
||||
end
|
||||
end
|
||||
return iter
|
||||
end
|
||||
|
||||
function creds_manager:reload_creds()
|
||||
cred_data = redis:hgetall(hash)
|
||||
end
|
||||
|
||||
function creds_manager:list_creds()
|
||||
creds_manager:reload_creds()
|
||||
if redis:exists("telegram:credentials") == true then
|
||||
local text = ""
|
||||
for var, key in pairsByKeys(cred_data) do
|
||||
text = text..var..' = '..key..'\n'
|
||||
end
|
||||
return text
|
||||
else
|
||||
create_cred()
|
||||
return "Es wurden noch keine Logininformationen gespeichert, lege Tabelle an...\nSpeichere Keys mit /creds add [Variable] [Key] ein!"
|
||||
end
|
||||
end
|
||||
|
||||
function creds_manager:add_creds(var, key)
|
||||
print('Saving credential for '..var..' to redis hash '..hash)
|
||||
redis:hset(hash, var, key)
|
||||
creds_manager:reload_creds()
|
||||
return 'Gespeichert!'
|
||||
end
|
||||
|
||||
function creds_manager:del_creds(var)
|
||||
if redis:hexists(hash, var) == true then
|
||||
print('Deleting credential for '..var..' from redis hash '..hash)
|
||||
redis:hdel(hash, var)
|
||||
creds_manager:reload_creds()
|
||||
return 'Key von "'..var..'" erfolgreich gelöscht!'
|
||||
else
|
||||
return 'Du hast keine Logininformationen für diese Variable eingespeichert.'
|
||||
end
|
||||
end
|
||||
|
||||
function creds_manager:rename_creds(var, newvar)
|
||||
if redis:hexists(hash, var) == true then
|
||||
local key = redis:hget(hash, var)
|
||||
if redis:hsetnx(hash, newvar, key) == true then
|
||||
redis:hdel(hash, var)
|
||||
creds_manager:reload_creds()
|
||||
return '"'..var..'" erfolgreich zu "'..newvar..'" umbenannt.'
|
||||
else
|
||||
return "Variable konnte nicht umbenannt werden: Zielvariable existiert bereits."
|
||||
end
|
||||
else
|
||||
return 'Die zu umbennende Variable existiert nicht.'
|
||||
end
|
||||
end
|
||||
|
||||
function creds_manager:action(msg, config, matches)
|
||||
local receiver = msg.from.id
|
||||
if receiver ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
|
||||
if msg.chat.type ~= 'private' then
|
||||
utilities.send_reply(self, msg, 'Dieses Plugin solltest du nur [privat](http://telegram.me/' .. self.info.username .. '?start=creds) verwenden!', true)
|
||||
return
|
||||
end
|
||||
|
||||
if matches[1] == "/creds" then
|
||||
utilities.send_reply(self, msg, creds_manager:list_creds())
|
||||
return
|
||||
elseif matches[1] == "/creds add" then
|
||||
local var = string.lower(string.sub(matches[2], 1, 50))
|
||||
local key = string.sub(matches[3], 1, 1000)
|
||||
utilities.send_reply(self, msg, creds_manager:add_creds(var, key))
|
||||
return
|
||||
elseif matches[1] == "/creds del" then
|
||||
local var = string.lower(matches[2])
|
||||
utilities.send_reply(self, msg, creds_manager:del_creds(var))
|
||||
return
|
||||
elseif matches[1] == "/creds rename" then
|
||||
local var = string.lower(string.sub(matches[2], 1, 50))
|
||||
local newvar = string.lower(string.sub(matches[3], 1, 1000))
|
||||
utilities.send_reply(self, msg, creds_manager:rename_creds(var, newvar))
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return creds_manager
|
@ -1,56 +0,0 @@
|
||||
local dice = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
dice.command = 'roll <nDr>'
|
||||
|
||||
function dice:init(config)
|
||||
dice.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('roll', true).table
|
||||
dice.doc = [[```
|
||||
]]..config.cmd_pat..[[roll <nDr>
|
||||
Returns a set of dice rolls, where n is the number of rolls and r is the range. If only a range is given, returns only one roll.
|
||||
```]]
|
||||
end
|
||||
|
||||
function dice:action(msg)
|
||||
|
||||
local input = utilities.input(msg.text_lower)
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, dice.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
|
||||
local count, range
|
||||
if input:match('^[%d]+d[%d]+$') then
|
||||
count, range = input:match('([%d]+)d([%d]+)')
|
||||
elseif input:match('^d?[%d]+$') then
|
||||
count = 1
|
||||
range = input:match('^d?([%d]+)$')
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, dice.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
|
||||
count = tonumber(count)
|
||||
range = tonumber(range)
|
||||
|
||||
if range < 2 then
|
||||
utilities.send_reply(self, msg, 'The minimum range is 2.')
|
||||
return
|
||||
end
|
||||
if range > 1000 or count > 1000 then
|
||||
utilities.send_reply(self, msg, 'The maximum range and count are 1000.')
|
||||
return
|
||||
end
|
||||
|
||||
local output = '*' .. count .. 'd' .. range .. '*\n`'
|
||||
for _ = 1, count do
|
||||
output = output .. math.random(range) .. '\t'
|
||||
end
|
||||
output = output .. '`'
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true)
|
||||
|
||||
end
|
||||
|
||||
return dice
|
37
otouto/plugins/expand.lua
Normal file
37
otouto/plugins/expand.lua
Normal file
@ -0,0 +1,37 @@
|
||||
local expand = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function expand:init(config)
|
||||
expand.triggers = {
|
||||
"^/expand (https?://[%w-_%.%?%.:/%+=&]+)$"
|
||||
}
|
||||
|
||||
expand.doc = [[*
|
||||
]]..config.cmd_pat..[[expand* _<Kurz-URL>_: Verlängert Kurz-URL (301er/302er)]]
|
||||
end
|
||||
|
||||
expand.command = 'expand <Kurz-URL>'
|
||||
|
||||
function expand:action(msg, config, matches)
|
||||
local response_body = {}
|
||||
local request_constructor = {
|
||||
url = matches[1],
|
||||
method = "HEAD",
|
||||
sink = ltn12.sink.table(response_body),
|
||||
headers = {},
|
||||
redirect = false
|
||||
}
|
||||
|
||||
local ok, response_code, response_headers, response_status_line = http.request(request_constructor)
|
||||
if ok and response_headers.location then
|
||||
utilities.send_reply(self, msg, response_headers.location)
|
||||
return
|
||||
else
|
||||
utilities.send_reply(self, msg, "Fehler beim Erweitern der URL.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return expand
|
180
otouto/plugins/facebook.lua
Normal file
180
otouto/plugins/facebook.lua
Normal file
@ -0,0 +1,180 @@
|
||||
local facebook = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function facebook:init(config)
|
||||
if not cred_data.fb_access_token then
|
||||
print('Missing config value: fb_access_token.')
|
||||
print('facebook.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
facebook.triggers = {
|
||||
"facebook.com/([A-Za-z0-9-._-]+)/(posts)/(%d+)",
|
||||
"facebook.com/(permalink).php%?(story_fbid)=(%d+)&id=(%d+)",
|
||||
"facebook.com/(photo).php%?fbid=(%d+)",
|
||||
"facebook.com/([A-Za-z0-9-._-]+)/(photos)/a.(%d+[%d%.]*)/(%d+)",
|
||||
"facebook.com/(video).php%?v=(%d+)",
|
||||
"facebook.com/([A-Za-z0-9-._-]+)/(videos)/(%d+[%d%.]*)",
|
||||
"facebook.com/([A-Za-z0-9-._-]+)"
|
||||
}
|
||||
end
|
||||
|
||||
local BASE_URL = 'https://graph.facebook.com/v2.5'
|
||||
local fb_access_token = cred_data.fb_access_token
|
||||
|
||||
local makeOurDate = function(dateString)
|
||||
local pattern = "(%d+)%/(%d+)%/(%d+)"
|
||||
local month, day, year = dateString:match(pattern)
|
||||
return day..'.'..month..'.'..year
|
||||
end
|
||||
|
||||
function facebook:get_fb_id(name)
|
||||
local url = BASE_URL..'/'..name..'?access_token='..fb_access_token..'&locale=de_DE'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return nil end
|
||||
local data = json.decode(res)
|
||||
return data.id
|
||||
end
|
||||
|
||||
function facebook:fb_post (id, story_id)
|
||||
local url = BASE_URL..'/'..id..'_'..story_id..'?access_token='..fb_access_token..'&locale=de_DE&fields=from,name,story,message,link'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return nil end
|
||||
local data = json.decode(res)
|
||||
|
||||
local from = data.from.name
|
||||
local message = data.message
|
||||
local name = data.name
|
||||
if data.link then
|
||||
link = '\n'..data.name..':\n'..data.link
|
||||
else
|
||||
link = ""
|
||||
end
|
||||
|
||||
if data.story then
|
||||
story = ' ('..data.story..')'
|
||||
else
|
||||
story = ""
|
||||
end
|
||||
|
||||
local text = '*'..from..'*'..story..':\n'..message..'\n'..link
|
||||
return text
|
||||
end
|
||||
|
||||
function facebook:send_facebook_photo(photo_id, receiver)
|
||||
local url = BASE_URL..'/'..photo_id..'?access_token='..fb_access_token..'&locale=de_DE&fields=images,from,name'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return nil end
|
||||
local data = json.decode(res)
|
||||
|
||||
local from = '*'..data.from.name..'*'
|
||||
if data.name then
|
||||
text = from..' hat ein Bild gepostet:\n'..data.name
|
||||
else
|
||||
text = from..' hat ein Bild gepostet:'
|
||||
end
|
||||
local image_url = data.images[1].source
|
||||
return text, image_url
|
||||
end
|
||||
|
||||
function facebook:send_facebook_video(video_id)
|
||||
local url = BASE_URL..'/'..video_id..'?access_token='..fb_access_token..'&locale=de_DE&fields=description,from,source,title'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return nil end
|
||||
local data = json.decode(res)
|
||||
|
||||
local from = '*'..data.from.name..'*'
|
||||
local description = data.description
|
||||
local source = data.source
|
||||
if data.title then
|
||||
text = from..' hat ein Video gepostet:\n'..description..'\n['..data.title..']('..source..')'
|
||||
else
|
||||
text = from..' hat ein Video gepostet:\n'..description..'\n'..utilities.md_escape(source)
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
function facebook:facebook_info(name)
|
||||
local url = BASE_URL..'/'..name..'?access_token='..fb_access_token..'&locale=de_DE&fields=about,name,birthday,category,founded,general_info,is_verified'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return nil end
|
||||
local data = json.decode(res)
|
||||
|
||||
local name = data.name
|
||||
if data.is_verified then
|
||||
name = name..' ✅'
|
||||
end
|
||||
|
||||
local category = data.category
|
||||
|
||||
if data.about then
|
||||
about = '\n'..data.about
|
||||
else
|
||||
about = ""
|
||||
end
|
||||
|
||||
if data.general_info then
|
||||
general_info = '\n'..data.general_info
|
||||
else
|
||||
general_info = ""
|
||||
end
|
||||
|
||||
if data.birthday and data.founded then
|
||||
birth = '\nGeburtstag: '..makeOurDate(data.birthday)
|
||||
elseif data.birthday and not data.founded then
|
||||
birth = '\nGeburtstag: '..makeOurDate(data.birthday)
|
||||
elseif data.founded and not data.birthday then
|
||||
birth = '\nGegründet: '..data.founded
|
||||
else
|
||||
birth = ""
|
||||
end
|
||||
|
||||
local text = '*'..name..'* ('..category..')_'..about..'_'..general_info..birth
|
||||
return text
|
||||
end
|
||||
|
||||
function facebook:action(msg, config, matches)
|
||||
if matches[1] == 'permalink' or matches[2] == 'posts' then
|
||||
story_id = matches[3]
|
||||
if not matches[4] then
|
||||
id = facebook:get_fb_id(matches[1])
|
||||
else
|
||||
id = matches[4]
|
||||
end
|
||||
utilities.send_reply(self, msg, facebook:fb_post(id, story_id), true)
|
||||
return
|
||||
elseif matches[1] == 'photo' or matches[2] == 'photos' then
|
||||
if not matches[4] then
|
||||
photo_id = matches[2]
|
||||
else
|
||||
photo_id = matches[4]
|
||||
end
|
||||
utilities.send_typing(self, msg.chat.id, 'upload_photo')
|
||||
local text, image_url = facebook:send_facebook_photo(photo_id, receiver)
|
||||
local file = download_to_file(image_url)
|
||||
utilities.send_reply(self, msg, text, true)
|
||||
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
|
||||
return
|
||||
elseif matches[1] == 'video' or matches[2] == 'videos' then
|
||||
if not matches[3] then
|
||||
video_id = matches[2]
|
||||
else
|
||||
video_id = matches[3]
|
||||
end
|
||||
local output = facebook:send_facebook_video(video_id)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
return
|
||||
else
|
||||
utilities.send_reply(self, msg, facebook:facebook_info(matches[1]), true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return facebook
|
222
otouto/plugins/forecast.lua
Normal file
222
otouto/plugins/forecast.lua
Normal file
@ -0,0 +1,222 @@
|
||||
local forecast = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function forecast:init(config)
|
||||
if not cred_data.forecastio_apikey then
|
||||
print('Missing config value: forecastio_apikey.')
|
||||
print('weather.lua will not be enabled.')
|
||||
return
|
||||
elseif not cred_data.google_apikey then
|
||||
print('Missing config value: google_apikey.')
|
||||
print('weather.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
forecast.triggers = {
|
||||
"^(/f)$",
|
||||
"^(/f) (.*)$",
|
||||
"^(/fh)$",
|
||||
"^(/fh) (.*)$",
|
||||
"^(/forecast)$",
|
||||
"^(/forecast) (.*)$",
|
||||
"^(/forecasth)$",
|
||||
"^(/forecasth) (.*)$"
|
||||
}
|
||||
forecast.doc = [[*
|
||||
]]..config.cmd_pat..[[f*: Wettervorhersage für deinen Wohnort _(/location set <Ort>)_
|
||||
*]]..config.cmd_pat..[[f* _<Ort>_: Wettervorhersage für diesen Ort
|
||||
*]]..config.cmd_pat..[[fh*: 24-Stunden-Wettervorhersage für deine Stadt _(/location set [Ort]_
|
||||
*]]..config.cmd_pat..[[fh* _<Ort>_: 24-Stunden-Wettervorhersage für diesen Ort
|
||||
]]
|
||||
end
|
||||
|
||||
forecast.command = 'forecast'
|
||||
|
||||
local BASE_URL = "https://api.forecast.io/forecast"
|
||||
local apikey = cred_data.forecastio_apikey
|
||||
local google_apikey = cred_data.google_apikey
|
||||
|
||||
function get_city_name(lat, lng)
|
||||
local city = redis:hget('telegram:cache:weather:pretty_names', lat..','..lng)
|
||||
if city then return city end
|
||||
local url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='..lat..','..lng..'&result_type=political&language=de&key='..google_apikey
|
||||
local res, code = HTTPS.request(url)
|
||||
if code ~= 200 then return 'Unbekannte Stadt' end
|
||||
local data = JSON.decode(res).results[1]
|
||||
local city = data.formatted_address
|
||||
print('Setting '..lat..','..lng..' in redis hash telegram:cache:weather:pretty_names to "'..city..'"')
|
||||
redis:hset('telegram:cache:weather:pretty_names', lat..','..lng, city)
|
||||
return city
|
||||
end
|
||||
|
||||
function get_condition_symbol(weather, n)
|
||||
if weather.data[n].icon == 'clear-day' then
|
||||
return '☀️'
|
||||
elseif weather.data[n].icon == 'clear-night' then
|
||||
return '🌙'
|
||||
elseif weather.data[n].icon == 'rain' then
|
||||
return '☔️'
|
||||
elseif weather.data[n].icon == 'snow' then
|
||||
return '❄️'
|
||||
elseif weather.data[n].icon == 'sleet' then
|
||||
return '🌨'
|
||||
elseif weather.data[n].icon == 'wind' then
|
||||
return '💨'
|
||||
elseif weather.data[n].icon == 'fog' then
|
||||
return '🌫'
|
||||
elseif weather.data[n].icon == 'cloudy' then
|
||||
return '☁️☁️'
|
||||
elseif weather.data[n].icon == 'partly-cloudy-day' then
|
||||
return '🌤'
|
||||
elseif weather.data[n].icon == 'partly-cloudy-night' then
|
||||
return '🌙☁️'
|
||||
else
|
||||
return ''
|
||||
end
|
||||
end
|
||||
|
||||
function get_temp(weather, n, hourly)
|
||||
if hourly then
|
||||
local temperature = string.gsub(round(weather.data[n].temperature, 1), "%.", ",")
|
||||
local condition = weather.data[n].summary
|
||||
return temperature..'°C | '..get_condition_symbol(weather, n)..' '..condition
|
||||
else
|
||||
local day = string.gsub(round(weather.data[n].temperatureMax, 1), "%.", ",")
|
||||
local night = string.gsub(round(weather.data[n].temperatureMin, 1), "%.", ",")
|
||||
local condition = weather.data[n].summary
|
||||
return '☀️ '..day..'°C | 🌙 '..night..'°C | '..get_condition_symbol(weather, n)..' '..condition
|
||||
end
|
||||
end
|
||||
|
||||
function forecast:get_forecast(lat, lng)
|
||||
print('Finde Wetter in '..lat..', '..lng)
|
||||
local text = redis:get('telegram:cache:forecast:'..lat..','..lng)
|
||||
if text then print('...aus dem Cache..') return text end
|
||||
|
||||
local url = BASE_URL..'/'..apikey..'/'..lat..','..lng..'?lang=de&units=si&exclude=currently,minutely,hourly,alerts,flags'
|
||||
|
||||
local response_body = {}
|
||||
local request_constructor = {
|
||||
url = url,
|
||||
method = "GET",
|
||||
sink = ltn12.sink.table(response_body)
|
||||
}
|
||||
local ok, response_code, response_headers, response_status_line = HTTPS.request(request_constructor)
|
||||
if not ok then return nil end
|
||||
local data = JSON.decode(table.concat(response_body))
|
||||
local ttl = string.sub(response_headers["cache-control"], 9)
|
||||
|
||||
|
||||
local weather = data.daily
|
||||
local city = get_city_name(lat, lng)
|
||||
|
||||
local header = '*Vorhersage für '..city..':*\n_'..weather.summary..'_\n'
|
||||
|
||||
local text = '*Heute:* '..get_temp(weather, 1)
|
||||
local text = text..'\n*Morgen:* '..get_temp(weather, 2)
|
||||
|
||||
for day in pairs(weather.data) do
|
||||
if day > 2 then
|
||||
text = text..'\n*'..convert_timestamp(weather.data[day].time, '%a, %d.%m')..'*: '..get_temp(weather, day)
|
||||
end
|
||||
end
|
||||
|
||||
local text = string.gsub(text, "Mon", "Mo")
|
||||
local text = string.gsub(text, "Tue", "Di")
|
||||
local text = string.gsub(text, "Wed", "Mi")
|
||||
local text = string.gsub(text, "Thu", "Do")
|
||||
local text = string.gsub(text, "Fri", "Fr")
|
||||
local text = string.gsub(text, "Sat", "Sa")
|
||||
local text = string.gsub(text, "Sun", "So")
|
||||
|
||||
cache_data('forecast', lat..','..lng, header..text, tonumber(ttl), 'key')
|
||||
|
||||
return header..text
|
||||
end
|
||||
|
||||
function forecast:get_forecast_hourly(lat, lng)
|
||||
print('Finde stündliches Wetter in '..lat..', '..lng)
|
||||
local text = redis:get('telegram:cache:forecast:'..lat..','..lng..':hourly')
|
||||
if text then print('...aus dem Cache..') return text end
|
||||
|
||||
local url = BASE_URL..'/'..apikey..'/'..lat..','..lng..'?lang=de&units=si&exclude=currently,minutely,daily,alerts,flags'
|
||||
|
||||
local response_body = {}
|
||||
local request_constructor = {
|
||||
url = url,
|
||||
method = "GET",
|
||||
sink = ltn12.sink.table(response_body)
|
||||
}
|
||||
local ok, response_code, response_headers, response_status_line = HTTPS.request(request_constructor)
|
||||
if not ok then return nil end
|
||||
local data = JSON.decode(table.concat(response_body))
|
||||
local ttl = string.sub(response_headers["cache-control"], 9)
|
||||
|
||||
|
||||
local weather = data.hourly
|
||||
local city = get_city_name(lat, lng)
|
||||
|
||||
local header = '*24-Stunden-Vorhersage für '..city..':*\n_'..weather.summary..'_'
|
||||
local text = ""
|
||||
|
||||
for hour in pairs(weather.data) do
|
||||
if hour < 26 then
|
||||
text = text..'\n*'..convert_timestamp(weather.data[hour].time, '%H:%M Uhr')..'* | '..get_temp(weather, hour, true)
|
||||
end
|
||||
end
|
||||
|
||||
cache_data('forecast', lat..','..lng..':hourly', header..text, tonumber(ttl), 'key')
|
||||
|
||||
return header..text
|
||||
end
|
||||
|
||||
function forecast:action(msg, config, matches)
|
||||
local user_id = msg.from.id
|
||||
local city = get_location(user_id)
|
||||
|
||||
if matches[2] then
|
||||
city = matches[2]
|
||||
else
|
||||
local set_location = get_location(user_id)
|
||||
if not set_location then
|
||||
city = 'Berlin, Deutschland'
|
||||
else
|
||||
city = set_location
|
||||
end
|
||||
end
|
||||
|
||||
local lat = redis:hget('telegram:cache:weather:'..string.lower(city), 'lat')
|
||||
local lng = redis:hget('telegram:cache:weather:'..string.lower(city), 'lng')
|
||||
if not lat and not lng then
|
||||
print('Koordinaten nicht eingespeichert, frage Google...')
|
||||
coords = utilities.get_coords(city, config)
|
||||
lat = coords.lat
|
||||
lng = coords.lon
|
||||
end
|
||||
|
||||
if not lat and not lng then
|
||||
utilities.send_reply(self, msg, '*Diesen Ort gibt es nicht!*', true)
|
||||
return
|
||||
end
|
||||
|
||||
redis:hset('telegram:cache:weather:'..string.lower(city), 'lat', lat)
|
||||
redis:hset('telegram:cache:weather:'..string.lower(city), 'lng', lng)
|
||||
|
||||
if matches[1] == '/forecasth' or matches[1] == '/fh' then
|
||||
text = forecast:get_forecast_hourly(lat, lng)
|
||||
else
|
||||
text = forecast:get_forecast(lat, lng)
|
||||
end
|
||||
if not text then
|
||||
text = '*Konnte die Wettervorhersage für diese Stadt nicht bekommen.*'
|
||||
end
|
||||
utilities.send_reply(self, msg, text, true)
|
||||
end
|
||||
|
||||
return forecast
|
@ -21,11 +21,10 @@ function gImages:init(config)
|
||||
end
|
||||
|
||||
gImages.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('img', true):t('i', true):t('insfw', true).table
|
||||
gImages.doc = [[```
|
||||
]]..config.cmd_pat..[[img <Suchbegriff>
|
||||
gImages.doc = [[*
|
||||
]]..config.cmd_pat..[[img* _<Suchbegriff>_
|
||||
Sucht Bild mit Google und versendet es (SafeSearch aktiv)
|
||||
Alias: ]]..config.cmd_pat..[[i
|
||||
```]]
|
||||
Alias: *]]..config.cmd_pat..[[i*]]
|
||||
end
|
||||
|
||||
gImages.command = 'img <Suchbegriff>'
|
||||
@ -47,6 +46,7 @@ function gImages:action(msg, config)
|
||||
return
|
||||
end
|
||||
|
||||
utilities.send_typing(self, msg.chat.id, 'upload_photo')
|
||||
local apikey = cred_data.google_apikey
|
||||
local cseid = cred_data.google_cse_id
|
||||
local BASE_URL = 'https://www.googleapis.com/customsearch/v1'
|
||||
@ -68,9 +68,7 @@ function gImages:action(msg, config)
|
||||
local img_url = jdat.items[i].link
|
||||
|
||||
local file = download_to_file(img_url)
|
||||
bindings.sendPhoto(self, {chat_id = msg.chat.id, caption = img_url}, {photo = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
utilities.send_photo(self, msg.chat.id, file, img_url)
|
||||
end
|
||||
|
||||
return gImages
|
||||
|
77
otouto/plugins/github.lua
Normal file
77
otouto/plugins/github.lua
Normal file
@ -0,0 +1,77 @@
|
||||
local github = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function github:init(config)
|
||||
github.triggers = {
|
||||
"github.com/([A-Za-z0-9-_-.-._.]+)/([A-Za-z0-9-_-.-._.]+)/commit/([a-z0-9-]+)",
|
||||
"github.com/([A-Za-z0-9-_-.-._.]+)/([A-Za-z0-9-_-.-._.]+)/?$"
|
||||
}
|
||||
end
|
||||
|
||||
local BASE_URL = 'https://api.github.com'
|
||||
|
||||
function github:get_gh_data(gh_code, gh_commit_sha)
|
||||
if gh_commit_sha == nil then
|
||||
url = BASE_URL..'/repos/'..gh_code
|
||||
else
|
||||
url = BASE_URL..'/repos/'..gh_code..'/git/commits/'..gh_commit_sha
|
||||
end
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return "HTTP-FEHLER" end
|
||||
local data = json.decode(res)
|
||||
return data
|
||||
end
|
||||
|
||||
function github:send_github_data(data)
|
||||
if not data.owner then return nil end
|
||||
local name = '*'..data.name..'*'
|
||||
local description = '_'..data.description..'_'
|
||||
local owner = data.owner.login
|
||||
local clone_url = data.clone_url
|
||||
if data.language == nil or data.language == "" then
|
||||
language = ''
|
||||
else
|
||||
language = '\nSprache: '..data.language
|
||||
end
|
||||
if data.open_issues_count == 0 then
|
||||
issues = ''
|
||||
else
|
||||
issues = '\nOffene Bugreports: '..data.open_issues_count
|
||||
end
|
||||
if data.homepage == nil or data.homepage == "" then
|
||||
homepage = ''
|
||||
else
|
||||
homepage = '\n[Homepage besuchen]('..data.homepage..')'
|
||||
end
|
||||
local text = name..' von '..owner..'\n'..description..'\n`git clone '..clone_url..'`'..language..issues..homepage
|
||||
return text
|
||||
end
|
||||
|
||||
function github:send_gh_commit_data(gh_code, gh_commit_sha, data)
|
||||
if not data.committer then return nil end
|
||||
local committer = data.committer.name
|
||||
local message = data.message
|
||||
local text = '`'..gh_code..'@'..gh_commit_sha..'` von *'..committer..'*:\n'..message
|
||||
return text
|
||||
end
|
||||
|
||||
function github:action(msg, config, matches)
|
||||
local gh_code = matches[1]..'/'..matches[2]
|
||||
local gh_commit_sha = matches[3]
|
||||
local data = github:get_gh_data(gh_code, gh_commit_sha)
|
||||
if not gh_commit_sha then
|
||||
output = github:send_github_data(data)
|
||||
else
|
||||
output = github:send_gh_commit_data(gh_code, gh_commit_sha, data)
|
||||
end
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
end
|
||||
|
||||
return github
|
16
otouto/plugins/images.lua
Normal file
16
otouto/plugins/images.lua
Normal file
@ -0,0 +1,16 @@
|
||||
local images = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
images.triggers = {
|
||||
"(https?://[%w-_%%%.%?%.:,/%+=~&%[%]]+%.[Pp][Nn][Gg])$",
|
||||
"(https?://[%w-_%%%.%?%.:,/%+=~&%[%]]+%.[Jj][Pp][Ee]?[Gg])$"
|
||||
}
|
||||
|
||||
function images:action(msg)
|
||||
utilities.send_typing(self, msg.chat.id, 'upload_photo')
|
||||
local url = matches[1]
|
||||
local file = download_to_file(url)
|
||||
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
|
||||
end
|
||||
|
||||
return images
|
@ -51,7 +51,7 @@ function imdb:action(msg, config)
|
||||
|
||||
if jdat.Poster ~= "N/A" then
|
||||
local file = download_to_file(jdat.Poster)
|
||||
bindings.sendPhoto(self, {chat_id = msg.chat.id}, {photo = file} )
|
||||
utilities.send_photo(self, msg.chat.id, file)
|
||||
end
|
||||
|
||||
end
|
||||
|
69
otouto/plugins/location_manager.lua
Normal file
69
otouto/plugins/location_manager.lua
Normal file
@ -0,0 +1,69 @@
|
||||
local loc_manager = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function loc_manager:init(config)
|
||||
loc_manager.triggers = {
|
||||
"^/location (set) (.*)$",
|
||||
"^/location (del)$",
|
||||
"^/location$"
|
||||
}
|
||||
loc_manager.doc = [[*
|
||||
]]..config.cmd_pat..[[location*: Gibt deinen gesetzten Wohnort aus
|
||||
*]]..config.cmd_pat..[[location* _set_ _<Ort>_: Setzt deinen Wohnort auf diesen Ort
|
||||
*]]..config.cmd_pat..[[location* _del_: Löscht deinen angegebenen Wohnort
|
||||
]]
|
||||
end
|
||||
|
||||
loc_manager.command = 'location'
|
||||
|
||||
function loc_manager:set_location(user_id, location)
|
||||
local hash = 'user:'..user_id
|
||||
local set_location = get_location(user_id)
|
||||
if set_location == location then
|
||||
return 'Dieser Ort wurde bereits gesetzt.'
|
||||
else
|
||||
print('Setting location in redis hash '..hash..' to location')
|
||||
redis:hset(hash, 'location', location)
|
||||
return 'Dein Wohnort wurde auf *'..location..'* festgelegt.'
|
||||
end
|
||||
end
|
||||
|
||||
function loc_manager:del_location(user_id)
|
||||
local hash = 'user:'..user_id
|
||||
local set_location = get_location(user_id)
|
||||
if not set_location then
|
||||
return 'Du hast keinen Ort gesetzt'
|
||||
else
|
||||
print('Setting location in redis hash '..hash..' to false')
|
||||
-- We set the location to false, because deleting the value blocks redis for a few milliseconds
|
||||
redis:hset(hash, 'location', false)
|
||||
return 'Dein Wohnort *'..set_location..'* wurde gelöscht!'
|
||||
end
|
||||
end
|
||||
|
||||
function loc_manager:action(msg, config, matches)
|
||||
local user_id = msg.from.id
|
||||
|
||||
if matches[1] == 'set' then
|
||||
utilities.send_reply(self, msg, loc_manager:set_location(user_id, matches[2]), true)
|
||||
return
|
||||
elseif matches[1] == 'del' then
|
||||
utilities.send_reply(self, msg, loc_manager:del_location(user_id), true)
|
||||
return
|
||||
else
|
||||
local set_location = get_location(user_id)
|
||||
if not set_location then
|
||||
utilities.send_reply(self, msg, '*Du hast keinen Ort gesetzt!*', true)
|
||||
return
|
||||
else
|
||||
local coords = utilities.get_coords(set_location, config)
|
||||
utilities.send_location(self, msg.chat.id, coords.lat, coords.lon, msg.message_id)
|
||||
utilities.send_reply(self, msg, 'Gesetzter Wohnort: *'..set_location..'*', true)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return loc_manager
|
67
otouto/plugins/media.lua
Normal file
67
otouto/plugins/media.lua
Normal file
@ -0,0 +1,67 @@
|
||||
local media = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local mimetype = (loadfile "./otouto/mimetype.lua")()
|
||||
|
||||
media.triggers = {
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(gif))$",
|
||||
"^(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(mp4))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(pdf))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(ogg))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(zip))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(tar.gz))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(7z))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(mp3))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(rar))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(wmv))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(doc))$",
|
||||
"^(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(avi))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(wav))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(apk))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(webm))$",
|
||||
"^(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(ogv))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(webp))$"
|
||||
}
|
||||
|
||||
function media:action(msg)
|
||||
local url = matches[1]
|
||||
local ext = matches[2]
|
||||
local receiver = msg.chat.id
|
||||
|
||||
utilities.send_typing(self, receiver, 'upload_document')
|
||||
local file = download_to_file(url)
|
||||
local mime_type = mimetype.get_content_type_no_sub(ext)
|
||||
|
||||
if ext == 'gif' then
|
||||
print('send gif')
|
||||
utilities.send_document(self, receiver, file, nil, msg.message_id)
|
||||
return
|
||||
|
||||
elseif mime_type == 'text' then
|
||||
print('send_document')
|
||||
utilities.send_document(self, receiver, file, nil, msg.message_id)
|
||||
return
|
||||
|
||||
elseif mime_type == 'image' then
|
||||
print('send_photo')
|
||||
utilities.send_photo(self, receiver, file, nil, msg.message_id)
|
||||
return
|
||||
|
||||
elseif mime_type == 'audio' then
|
||||
print('send_audio')
|
||||
utilities.send_audio(self, receiver, file, nil, msg.message_id)
|
||||
return
|
||||
|
||||
elseif mime_type == 'video' then
|
||||
print('send_video')
|
||||
utilities.send_video(self, receiver, file, nil, msg.message_id)
|
||||
return
|
||||
|
||||
else
|
||||
print('send_file')
|
||||
utilities.send_document(self, receiver, file, nil, msg.message_id)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return media
|
45
otouto/plugins/pasteee.lua
Normal file
45
otouto/plugins/pasteee.lua
Normal file
@ -0,0 +1,45 @@
|
||||
local pasteee = {}
|
||||
|
||||
local bot = require('otouto.bot')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function pasteee:init(config)
|
||||
if not cred_data.pasteee_key then
|
||||
print('Missing config value: pasteee_key.')
|
||||
print('pasteee.lua will not be enabled, listquotes won\'t be available.')
|
||||
return
|
||||
end
|
||||
|
||||
pasteee.triggers = {
|
||||
"^/pasteee (.*)$"
|
||||
}
|
||||
pasteee.doc = [[*
|
||||
]]..config.cmd_pat..[[pasteee* _<Text>_: Postet Text auf Paste.ee]]
|
||||
end
|
||||
|
||||
pasteee.command = 'pasteee <Text>'
|
||||
|
||||
local key = cred_data.pasteee_key
|
||||
|
||||
function upload(text, noraw)
|
||||
local url = "https://paste.ee/api"
|
||||
local pet = post_petition(url, 'key='..key..'&paste='..text..'&format=json')
|
||||
if pet.status ~= 'success' then return 'Ein Fehler ist aufgetreten: '..pet.error, true end
|
||||
if noraw then
|
||||
return pet.paste.link
|
||||
else
|
||||
return pet.paste.raw
|
||||
end
|
||||
end
|
||||
|
||||
function pasteee:action(msg, config, matches)
|
||||
local text = matches[1]
|
||||
local link, iserror = upload(text)
|
||||
if iserror then
|
||||
utilities.send_reply(self, msg, link)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, '[Text auf Paste.ee ansehen]('..link..')', true)
|
||||
end
|
||||
|
||||
return pasteee
|
@ -7,7 +7,7 @@ patterns.triggers = {
|
||||
}
|
||||
|
||||
function patterns:action(msg)
|
||||
if not msg.reply_to_message then return end
|
||||
if not msg.reply_to_message then return true end
|
||||
local output = msg.reply_to_message.text
|
||||
if msg.reply_to_message.from.id == self.info.id then
|
||||
output = output:gsub('Did you mean:\n"', '')
|
||||
@ -22,8 +22,7 @@ function patterns:action(msg)
|
||||
end
|
||||
)
|
||||
if res == false then
|
||||
output = 'Malformed pattern!'
|
||||
utilities.send_reply(self, msg, output)
|
||||
utilities.send_reply(self, msg, 'Malformed pattern!')
|
||||
else
|
||||
output = output:sub(1, 4000)
|
||||
output = 'Did you mean:\n"' .. output .. '"'
|
||||
|
111
otouto/plugins/quotes.lua
Normal file
111
otouto/plugins/quotes.lua
Normal file
@ -0,0 +1,111 @@
|
||||
local quotes = {}
|
||||
|
||||
local bot = require('otouto.bot')
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
require("./otouto/plugins/pasteee")
|
||||
|
||||
function quotes:init(config)
|
||||
quotes.triggers = {
|
||||
"^/(delquote) (.+)$",
|
||||
"^/(addquote) (.+)$",
|
||||
"^/(quote)$",
|
||||
"^/(listquotes)$"
|
||||
}
|
||||
quotes.doc = [[*
|
||||
]]..config.cmd_pat..[[addquote* _<Zitat>_: Fügt Zitat hinzu.
|
||||
*]]..config.cmd_pat..[[delquote* _<Zitat>_: Löscht das Zitat (nur Superuser)
|
||||
*]]..config.cmd_pat..[[quote*: Gibt zufälliges Zitat aus
|
||||
*]]..config.cmd_pat..[[listquotes*: Listet alle Zitate auf
|
||||
]]
|
||||
end
|
||||
|
||||
quotes.command = 'quote'
|
||||
|
||||
function quotes:save_quote(msg)
|
||||
if msg.text:sub(11):isempty() then
|
||||
return "Benutzung: /addquote [Zitat]"
|
||||
end
|
||||
|
||||
local quote = msg.text:sub(11)
|
||||
local hash = get_redis_hash(msg, 'quotes')
|
||||
print('Saving quote to redis set '..hash)
|
||||
redis:sadd(hash, quote)
|
||||
return '*Gespeichert!*'
|
||||
end
|
||||
|
||||
function quotes:delete_quote(msg)
|
||||
if msg.text:sub(11):isempty() then
|
||||
return "Benutzung: /delquote [Zitat]"
|
||||
end
|
||||
|
||||
local quote = msg.text:sub(11)
|
||||
local hash = get_redis_hash(msg, 'quotes')
|
||||
print('Deleting quote from redis set '..hash)
|
||||
if redis:sismember(hash, quote) == true then
|
||||
redis:srem(hash, quote)
|
||||
return '*Zitat erfolgreich gelöscht!*'
|
||||
else
|
||||
return 'Dieses Zitat existiert nicht.'
|
||||
end
|
||||
end
|
||||
|
||||
function quotes:get_quote(msg)
|
||||
local hash = get_redis_hash(msg, 'quotes')
|
||||
|
||||
if hash then
|
||||
print('Getting quote from redis set '..hash)
|
||||
local quotes_table = redis:smembers(hash)
|
||||
if not quotes_table[1] then
|
||||
return 'Es wurden noch keine Zitate gespeichert.\nSpeichere doch welche mit /addquote [Zitat]'
|
||||
else
|
||||
return quotes_table[math.random(1,#quotes_table)]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function quotes:list_quotes(msg)
|
||||
local hash = get_redis_hash(msg, 'quotes')
|
||||
|
||||
if hash then
|
||||
print('Getting quotes from redis set '..hash)
|
||||
local quotes_table = redis:smembers(hash)
|
||||
local text = ""
|
||||
for num,quote in pairs(quotes_table) do
|
||||
text = text..num..") "..quote..'\n'
|
||||
end
|
||||
if not text or text == "" then
|
||||
return 'Es wurden noch keine Zitate gespeichert.\nSpeichere doch welche mit !addquote [Zitat]'
|
||||
else
|
||||
return upload(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function quotes:action(msg, config, matches)
|
||||
if matches[1] == "quote" then
|
||||
utilities.send_message(self, msg.chat.id, quotes:get_quote(msg), true)
|
||||
return
|
||||
elseif matches[1] == "addquote" and matches[2] then
|
||||
utilities.send_reply(self, msg, quotes:save_quote(msg), true)
|
||||
return
|
||||
elseif matches[1] == "delquote" and matches[2] then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, quotes:delete_quote(msg), true)
|
||||
return
|
||||
elseif matches[1] == "listquotes" then
|
||||
local link, iserror = quotes:list_quotes(msg)
|
||||
if iserror then
|
||||
utilities.send_reply(self, msg, link)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, '[Lise aller Zitate auf Paste.ee ansehen]('..link..')', true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, quotes.doc, true)
|
||||
end
|
||||
|
||||
return quotes
|
86
otouto/plugins/respond.lua
Normal file
86
otouto/plugins/respond.lua
Normal file
@ -0,0 +1,86 @@
|
||||
local respond = {}
|
||||
|
||||
local https = require('ssl.https')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
|
||||
function respond:init(config)
|
||||
respond.triggers = {
|
||||
"([Ff][Gg][Tt].? [Ss][Ww][Ii][Ff][Tt])",
|
||||
"([Ee][Ii][Nn][Zz][Ii][Gg][Ss][Tt][Ee][Ss])",
|
||||
"([Ee][Ii][Nn][Zz][Ii][Gg][Ss][Tt][Ee][Rr])",
|
||||
"([Ee][Ii][Nn][Zz][Ii][Gg][Ss][Tt][Ee])",
|
||||
"^[Bb][Oo][Tt]%??$",
|
||||
"^/([Ll][Oo][Dd])$",
|
||||
"^/([Ll][Ff])$",
|
||||
"^/([Kk][Aa])$",
|
||||
"^/([Ii][Dd][Kk])$",
|
||||
"^/([Nn][Bb][Cc])$",
|
||||
"^/([Ii][Dd][Cc])$",
|
||||
"^%*([Ff][Rr][Oo][Ss][Cc][Hh])%*",
|
||||
"^/([Ff][Rr][Oo][Ss][Cc][Hh])$",
|
||||
"^%(([Ii][Nn][Ll][Oo][Vv][Ee])%)$",
|
||||
"^/[Ww][Aa][Tt]$"
|
||||
}
|
||||
end
|
||||
|
||||
respond.command = 'lod, /lf, /nbc, /wat'
|
||||
|
||||
function respond:action(msg, config, matches)
|
||||
local user_name = get_name(msg)
|
||||
local receiver = msg.chat.id
|
||||
local GDRIVE_URL = 'https://de2319bd4b4b51a5ef2939a7638c1d35646f49f8.googledrive.com/host/0B_mfIlDgPiyqU25vUHZqZE9IUXc'
|
||||
if user_name == "DefenderX" then user_name = "Deffu" end
|
||||
|
||||
if string.match(msg.text, "[Ff][Gg][Tt].? [Ss][Ww][Ii][Ff][Tt]") then
|
||||
utilities.send_message(self, receiver, 'Dünnes Eis, '..user_name..'!')
|
||||
return
|
||||
elseif string.match(msg.text, "([Ee][Ii][Nn][Zz][Ii][Gg][Ss][Tt][Ee][Ss])") then
|
||||
utilities.send_message(self, receiver, '*einziges')
|
||||
return
|
||||
elseif string.match(msg.text, "([Ee][Ii][Nn][Zz][Ii][Gg][Ss][Tt][Ee][Rr])") then
|
||||
utilities.send_message(self, receiver, '*einziger')
|
||||
return
|
||||
elseif string.match(msg.text, "([Ee][Ii][Nn][Zz][Ii][Gg][Ss][Tt][Ee])") then
|
||||
utilities.send_message(self, receiver, '*einzige')
|
||||
return
|
||||
elseif string.match(msg.text, "[Bb][Oo][Tt]%??") then
|
||||
utilities.send_reply(self, msg, '*Ich bin da, '..user_name..'!*', true)
|
||||
return
|
||||
elseif string.match(msg.text, "[Ll][Oo][Dd]") then
|
||||
utilities.send_message(self, receiver, 'ಠ_ಠ')
|
||||
return
|
||||
elseif string.match(msg.text, "[Ll][Ff]") then
|
||||
utilities.send_message(self, receiver, '( ͡° ͜ʖ ͡°)')
|
||||
return
|
||||
elseif string.match(msg.text, "[Nn][Bb][Cc]") or string.match(msg.text, "[Ii][Dd][Cc]") or string.match(msg.text, "[Kk][Aa]") or string.match(msg.text, "[Ii][Dd][Kk]") then
|
||||
utilities.send_message(self, receiver, [[¯\_(ツ)_/¯]])
|
||||
return
|
||||
elseif string.match(msg.text, "[Ff][Rr][Oo][Ss][Cc][Hh]") then
|
||||
utilities.send_message(self, receiver, '🐸🐸🐸')
|
||||
return
|
||||
elseif string.match(msg.text, "[Ii][Nn][Ll][Oo][Vv][Ee]") then
|
||||
local file = download_to_file(GDRIVE_URL..'/inlove.gif')
|
||||
utilities.send_document(self, receiver, file)
|
||||
return
|
||||
elseif string.match(msg.text, "[Ww][Aa][Tt]") then
|
||||
local WAT_URL = GDRIVE_URL..'/wat'
|
||||
local wats = {
|
||||
"/wat1.jpg",
|
||||
"/wat2.jpg",
|
||||
"/wat3.jpg",
|
||||
"/wat4.jpg",
|
||||
"/wat5.jpg",
|
||||
"/wat6.jpg",
|
||||
"/wat7.jpg",
|
||||
"/wat8.jpg"
|
||||
}
|
||||
local random_wat = math.random(5)
|
||||
local file = download_to_file(WAT_URL..wats[random_wat])
|
||||
utilities.send_photo(self, receiver, file)
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return respond
|
31
otouto/plugins/roll.lua
Normal file
31
otouto/plugins/roll.lua
Normal file
@ -0,0 +1,31 @@
|
||||
local roll = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
roll.command = 'roll'
|
||||
|
||||
function roll:init(config)
|
||||
roll.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('roll', true).table
|
||||
roll.doc = [[*
|
||||
]]..config.cmd_pat..[[roll*: Werfe einen Würfel]]
|
||||
end
|
||||
|
||||
local canroll = {
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6"
|
||||
}
|
||||
|
||||
function roll:roll_dice()
|
||||
local randomroll = math.random(6)
|
||||
return canroll[randomroll]
|
||||
end
|
||||
|
||||
function roll:action(msg)
|
||||
utilities.send_reply(self, msg, 'Du hast eine *'..roll:roll_dice()..'* gewürfelt.', true)
|
||||
end
|
||||
|
||||
return roll
|
112
otouto/plugins/tagesschau_eil.lua
Normal file
112
otouto/plugins/tagesschau_eil.lua
Normal file
@ -0,0 +1,112 @@
|
||||
local tagesschau_eil = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
local url = require('socket.url')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
tagesschau_eil.command = 'eil <sub/del>'
|
||||
|
||||
function tagesschau_eil:init(config)
|
||||
tagesschau_eil.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('eil', true).table
|
||||
tagesschau_eil.doc = [[*
|
||||
]]..config.cmd_pat..[[eil* _sub_: Eilmeldungen abonnieren
|
||||
*]]..config.cmd_pat..[[eil* _del_: Eilmeldungen deabonnieren
|
||||
*]]..config.cmd_pat..[[eil* _sync_: Nach neuen Eilmeldungen prüfen (nur Superuser)]]
|
||||
end
|
||||
|
||||
local makeOurDate = function(dateString)
|
||||
local pattern = "(%d+)%-(%d+)%-(%d+)T(%d+)%:(%d+)%:(%d+)"
|
||||
local year, month, day, hours, minutes, seconds = dateString:match(pattern)
|
||||
return day..'.'..month..'.'..year..' um '..hours..':'..minutes..':'..seconds
|
||||
end
|
||||
|
||||
local url = 'http://www.tagesschau.de/api'
|
||||
local hash = 'telegram:tagesschau'
|
||||
|
||||
function tagesschau_eil:abonnieren(id)
|
||||
if redis:sismember(hash..':subs', id) == false then
|
||||
redis:sadd(hash..':subs', id)
|
||||
return '*Eilmeldungen abonniert.*'
|
||||
else
|
||||
return 'Die Eilmeldungen wurden hier bereits abonniert.'
|
||||
end
|
||||
end
|
||||
|
||||
function tagesschau_eil:deabonnieren(id)
|
||||
if redis:sismember(hash..':subs', id) == true then
|
||||
redis:srem(hash..':subs', id)
|
||||
return '*Eilmeldungen deabonniert.*'
|
||||
else
|
||||
return 'Die Eilmeldungen wurden hier noch nicht abonniert.'
|
||||
end
|
||||
end
|
||||
|
||||
function tagesschau_eil:action(msg, config)
|
||||
local input = utilities.input(msg.text)
|
||||
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, tagesschau_eil.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local id = "user#id" .. msg.from.id
|
||||
if msg.chat.type == 'channel' then
|
||||
print('Kanäle werden momentan nicht unterstützt')
|
||||
end
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
id = 'chat#id'..msg.chat.id
|
||||
end
|
||||
|
||||
if input:match('(sub)$') then
|
||||
local output = tagesschau_eil:abonnieren(id)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
elseif input:match('(del)$') then
|
||||
local output = tagesschau_eil:deabonnieren(id)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
elseif input:match('(sync)$') then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
tagesschau_eil:cron(self)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
function tagesschau_eil:cron(self_plz)
|
||||
if not self.BASE_URL then
|
||||
self = self_plz
|
||||
end
|
||||
-- print('EIL: Prüfe...')
|
||||
local last_eil = redis:get(hash..':last_entry')
|
||||
local res,code = http.request(url)
|
||||
local data = json.decode(res)
|
||||
if code ~= 200 then return end
|
||||
if not data then return end
|
||||
if data.breakingnews[1] then
|
||||
if data.breakingnews[1].details ~= last_eil then
|
||||
local title = '#EIL: *'..data.breakingnews[1].headline..'*'
|
||||
local news = data.breakingnews[1].shorttext
|
||||
local posted_at = makeOurDate(data.breakingnews[1].date)..' Uhr'
|
||||
local post_url = string.gsub(data.breakingnews[1].details, '/api/', '/')
|
||||
local post_url = string.gsub(post_url, '.json', '.html')
|
||||
local eil = title..'\n_'..posted_at..'_\n'..news..'\n[Artikel aufrufen]('..post_url..')'
|
||||
redis:set(hash..':last_entry', data.breakingnews[1].details)
|
||||
for _,user in pairs(redis:smembers(hash..':subs')) do
|
||||
local user = string.gsub(user, 'chat%#id', '')
|
||||
local user = string.gsub(user, 'user%#id', '')
|
||||
utilities.send_message(self, user, eil, true, nil, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return tagesschau_eil
|
@ -140,15 +140,11 @@ function twitter:action(msg)
|
||||
utilities.send_reply(self, msg, header .. "\n" .. text.."\n"..footer)
|
||||
for k, v in pairs(images) do
|
||||
local file = download_to_file(v)
|
||||
bindings.sendPhoto(self, {chat_id = msg.chat.id}, {photo = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
|
||||
end
|
||||
for k, v in pairs(videos) do
|
||||
local file = download_to_file(v)
|
||||
bindings.sendVideo(self, {chat_id = msg.chat.id}, {video = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
utilities.send_video(self, msg.chat.id, file, nil, msg.message_id)
|
||||
end
|
||||
end
|
||||
|
||||
|
349
otouto/plugins/twitter_send.lua
Normal file
349
otouto/plugins/twitter_send.lua
Normal file
@ -0,0 +1,349 @@
|
||||
local twitter_send = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local OAuth = require "OAuth"
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function twitter_send:init(config)
|
||||
if not cred_data.tw_consumer_key then
|
||||
print('Missing config value: tw_consumer_key.')
|
||||
print('twitter_send.lua will not be enabled.')
|
||||
return
|
||||
elseif not cred_data.tw_consumer_secret then
|
||||
print('Missing config value: tw_consumer_secret.')
|
||||
print('twitter_send.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
twitter_send.triggers = {
|
||||
"^/tw (auth) (%d+)",
|
||||
"^/tw (unauth)$",
|
||||
"^/tw (verify)$",
|
||||
"^/tw (.+)",
|
||||
"^/(twwhitelist add) (%d+)",
|
||||
"^/(twwhitelist del) (%d+)"
|
||||
}
|
||||
twitter_send.doc = [[*
|
||||
]]..config.cmd_pat..[[tw* _<Text>_: Sendet einen Tweet an den Account, der im Chat angemeldet ist
|
||||
*]]..config.cmd_pat..[[tw* _verify_: Gibt den angemeldeten User aus, inklusive Profilbild
|
||||
*]]..config.cmd_pat..[[twwitelist* _add_ _<user>_: Schaltet User für die Tweet-Funktion frei
|
||||
*]]..config.cmd_pat..[[twwitelist* _del_ _<user>_: Entfernt User von der Tweet-Whitelist
|
||||
*]]..config.cmd_pat..[[tw* _auth_ _<PIN>_: Meldet mit dieser PIN an (Setup)
|
||||
*]]..config.cmd_pat..[[tw* _unauth_: Meldet Twitter-Account ab
|
||||
]]
|
||||
end
|
||||
|
||||
twitter_send.command = 'tw <Tweet>'
|
||||
|
||||
local consumer_key = cred_data.tw_consumer_key
|
||||
local consumer_secret = cred_data.tw_consumer_secret
|
||||
|
||||
function can_send_tweet(msg)
|
||||
local hash = 'user:'..msg.from.id
|
||||
local var = redis:hget(hash, 'can_send_tweet')
|
||||
if var == "true" then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local client = OAuth.new(consumer_key, consumer_secret, {
|
||||
RequestToken = "https://api.twitter.com/oauth/request_token",
|
||||
AuthorizeUser = {"https://api.twitter.com/oauth/authorize", method = "GET"},
|
||||
AccessToken = "https://api.twitter.com/oauth/access_token"
|
||||
})
|
||||
|
||||
function twitter_send:do_twitter_authorization_flow(hash, is_chat)
|
||||
local callback_url = "oob"
|
||||
local values = client:RequestToken({ oauth_callback = callback_url })
|
||||
local oauth_token = values.oauth_token
|
||||
local oauth_token_secret = values.oauth_token_secret
|
||||
|
||||
-- save temporary oauth keys
|
||||
redis:hset(hash, 'oauth_token', oauth_token)
|
||||
redis:hset(hash, 'oauth_token_secret', oauth_token_secret)
|
||||
|
||||
local auth_url = client:BuildAuthorizationUrl({ oauth_callback = callback_url, force_login = true })
|
||||
if is_chat then
|
||||
return 'Bitte schließe den Vorgang ab, indem du unten auf den Link klickst und mir die angezeigte PIN per `/tw auth PIN` *im Chat von gerade* übergibst.\n[Bei Twitter anmelden]('..auth_url..')'
|
||||
else
|
||||
return 'Bitte schließe den Vorgang ab, indem du unten auf den Link klickst und mir die angezeigte PIN per `/tw auth PIN` übergibst.\n[Bei Twitter anmelden]('..auth_url..')'
|
||||
end
|
||||
end
|
||||
|
||||
function twitter_send:get_twitter_access_token(hash, oauth_verifier, oauth_token, oauth_token_secret)
|
||||
local oauth_verifier = tostring(oauth_verifier) -- must be a string
|
||||
|
||||
-- now we'll use the tokens we got in the RequestToken call, plus our PIN
|
||||
local client = OAuth.new(consumer_key, consumer_secret, {
|
||||
RequestToken = "https://api.twitter.com/oauth/request_token",
|
||||
AuthorizeUser = {"https://api.twitter.com/oauth/authorize", method = "GET"},
|
||||
AccessToken = "https://api.twitter.com/oauth/access_token"
|
||||
}, {
|
||||
OAuthToken = oauth_token,
|
||||
OAuthVerifier = oauth_verifier
|
||||
})
|
||||
client:SetTokenSecret(oauth_token_secret)
|
||||
|
||||
local values, err, headers, status, body = client:GetAccessToken()
|
||||
if err then return 'Einloggen fehlgeschlagen!' end
|
||||
|
||||
-- save permanent oauth keys
|
||||
redis:hset(hash, 'oauth_token', values.oauth_token)
|
||||
redis:hset(hash, 'oauth_token_secret', values.oauth_token_secret)
|
||||
|
||||
return 'Erfolgreich eingeloggt als "@'..values.screen_name..'" (User-ID: '..values.user_id..')'
|
||||
end
|
||||
|
||||
function twitter_send:reset_twitter_auth(hash, frominvalid)
|
||||
redis:hdel(hash, 'oauth_token')
|
||||
redis:hdel(hash, 'oauth_token_secret')
|
||||
if frominvalid then
|
||||
return '*Authentifizierung nicht erfolgreich, wird zurückgesetzt...*'
|
||||
else
|
||||
return '*Erfolgreich abgemeldet!* Entziehe den Zugriff endgültig in deinen [Twitter-Einstellungen](https://twitter.com/settings/applications)!'
|
||||
end
|
||||
end
|
||||
|
||||
function twitter_send:resolve_url(url)
|
||||
local response_body = {}
|
||||
local request_constructor = {
|
||||
url = url,
|
||||
method = "HEAD",
|
||||
sink = ltn12.sink.table(response_body),
|
||||
headers = {},
|
||||
redirect = false
|
||||
}
|
||||
|
||||
local ok, response_code, response_headers, response_status_line = http.request(request_constructor)
|
||||
if ok and response_headers.location then
|
||||
return response_headers.location
|
||||
else
|
||||
return url
|
||||
end
|
||||
end
|
||||
|
||||
function twitter_send:twitter_verify_credentials(oauth_token, oauth_token_secret)
|
||||
local client = OAuth.new(consumer_key, consumer_secret, {
|
||||
RequestToken = "https://api.twitter.com/oauth/request_token",
|
||||
AuthorizeUser = {"https://api.twitter.com/oauth/authorize", method = "GET"},
|
||||
AccessToken = "https://api.twitter.com/oauth/access_token"
|
||||
}, {
|
||||
OAuthToken = oauth_token,
|
||||
OAuthTokenSecret = oauth_token_secret
|
||||
})
|
||||
|
||||
local response_code, response_headers, response_status_line, response_body =
|
||||
client:PerformRequest(
|
||||
"GET", "https://api.twitter.com/1.1/account/verify_credentials.json", {
|
||||
include_entities = false,
|
||||
skip_status = true,
|
||||
include_email = false
|
||||
}
|
||||
)
|
||||
|
||||
local response = json.decode(response_body)
|
||||
if response_code == 401 then
|
||||
return twitter_send:reset_twitter_auth(hash, true)
|
||||
end
|
||||
if response_code ~= 200 then
|
||||
return 'HTTP-Fehler '..response_code..': '..data.errors[1].message
|
||||
end
|
||||
|
||||
-- TODO: copied straight from the twitter_user plugin, maybe we can do it better?
|
||||
local full_name = response.name
|
||||
local user_name = response.screen_name
|
||||
if response.verified then
|
||||
user_name = user_name..' ✅'
|
||||
end
|
||||
if response.protected then
|
||||
user_name = user_name..' 🔒'
|
||||
end
|
||||
local header = full_name.. " (@" ..user_name.. ")\n"
|
||||
|
||||
local description = unescape(response.description)
|
||||
if response.location then
|
||||
location = response.location
|
||||
else
|
||||
location = ''
|
||||
end
|
||||
if response.url and response.location ~= '' then
|
||||
url = ' | '..twitter_send:resolve_url(response.url)..'\n'
|
||||
elseif response.url and response.location == '' then
|
||||
url = twitter_send:resolve_url(response.url)..'\n'
|
||||
else
|
||||
url = '\n'
|
||||
end
|
||||
|
||||
local body = description..'\n'..location..url
|
||||
|
||||
local favorites = comma_value(response.favourites_count)
|
||||
local follower = comma_value(response.followers_count)
|
||||
local following = comma_value(response.friends_count)
|
||||
local statuses = comma_value(response.statuses_count)
|
||||
local footer = statuses..' Tweets, '..follower..' Follower, '..following..' folge ich, '..favorites..' Tweets favorisiert'
|
||||
|
||||
local text = 'Eingeloggter Account:\n'..header..body..footer
|
||||
local pp_url = string.gsub(response.profile_image_url_https, "normal", "400x400")
|
||||
|
||||
return text, pp_url
|
||||
end
|
||||
|
||||
function twitter_send:send_tweet(tweet, oauth_token, oauth_token_secret, hash)
|
||||
local client = OAuth.new(consumer_key, consumer_secret, {
|
||||
RequestToken = "https://api.twitter.com/oauth/request_token",
|
||||
AuthorizeUser = {"https://api.twitter.com/oauth/authorize", method = "GET"},
|
||||
AccessToken = "https://api.twitter.com/oauth/access_token"
|
||||
}, {
|
||||
OAuthToken = oauth_token,
|
||||
OAuthTokenSecret = oauth_token_secret
|
||||
})
|
||||
|
||||
local response_code, response_headers, response_status_line, response_body =
|
||||
client:PerformRequest(
|
||||
"POST", "https://api.twitter.com/1.1/statuses/update.json", {
|
||||
status = tweet
|
||||
}
|
||||
)
|
||||
|
||||
local data = json.decode(response_body)
|
||||
if response_code == 401 then
|
||||
return twitter_send:reset_twitter_auth(hash, true)
|
||||
end
|
||||
if response_code ~= 200 then
|
||||
return 'HTTP-Fehler '..response_code..': '..data.errors[1].message
|
||||
end
|
||||
|
||||
local statusnumber = comma_value(data.user.statuses_count)
|
||||
local screen_name = data.user.screen_name
|
||||
local status_id = data.id_str
|
||||
|
||||
return '*Tweet #'..statusnumber..' gesendet!* [Auf Twitter ansehen](https://twitter.com/statuses/'..status_id..')'
|
||||
end
|
||||
|
||||
function twitter_send:add_to_twitter_whitelist(user_id)
|
||||
local hash = 'user:'..user_id
|
||||
local whitelisted = redis:hget(hash, 'can_send_tweet')
|
||||
if whitelisted ~= 'true' then
|
||||
print('Setting can_send_tweet in redis hash '..hash..' to true')
|
||||
redis:hset(hash, 'can_send_tweet', true)
|
||||
return '*User '..user_id..' kann jetzt Tweets senden!*'
|
||||
else
|
||||
return '*User '..user_id..' kann schon Tweets senden.*'
|
||||
end
|
||||
end
|
||||
|
||||
function twitter_send:del_from_twitter_whitelist(user_id)
|
||||
local hash = 'user:'..user_id
|
||||
local whitelisted = redis:hget(hash, 'can_send_tweet')
|
||||
if whitelisted == 'true' then
|
||||
print('Setting can_send_tweet in redis hash '..hash..' to false')
|
||||
redis:hset(hash, 'can_send_tweet', false)
|
||||
return '*User '..user_id..' kann jetzt keine Tweets mehr senden!*'
|
||||
else
|
||||
return '*User '..user_id..' ist nicht whitelisted.*'
|
||||
end
|
||||
end
|
||||
|
||||
function twitter_send:action(msg, config, matches)
|
||||
if matches[1] == "twwhitelist add" and matches[2] then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
else
|
||||
utilities.send_reply(self, msg, twitter_send:add_to_twitter_whitelist(matches[2]), true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if matches[1] == "twwhitelist del" and matches[2] then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
else
|
||||
utilities.send_reply(self, msg, twitter_send:del_from_twitter_whitelist(matches[2]), true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local hash = get_redis_hash(msg, 'twitter')
|
||||
local oauth_token = redis:hget(hash, 'oauth_token')
|
||||
local oauth_token_secret = redis:hget(hash, 'oauth_token_secret')
|
||||
|
||||
-- Thanks to the great doc at https://github.com/ignacio/LuaOAuth#a-more-involved-example
|
||||
if not oauth_token and not oauth_token_secret then
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
else
|
||||
-- maybe we can edit the older message to update it to "logged in!"?
|
||||
-- this should be interesting: https://core.telegram.org/bots/api#editmessagetext
|
||||
local text = twitter_send:do_twitter_authorization_flow(hash, true)
|
||||
local res = utilities.send_message(self, msg.from.id, text, true, nil, true)
|
||||
if not res then
|
||||
utilities.send_reply(self, msg, 'Bitte starte mich zuerst [privat](http://telegram.me/' .. self.info.username .. '?start).', true)
|
||||
elseif msg.chat.type ~= 'private' then
|
||||
utilities.send_message(self, msg.chat.id, '_Bitte warten, der Administrator meldet sich an..._', true, nil, true)
|
||||
end
|
||||
return
|
||||
end
|
||||
else
|
||||
utilities.send_reply(self, msg, twitter_send:do_twitter_authorization_flow(hash), true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if matches[1] == 'auth' and matches[2] then
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
end
|
||||
if string.len(matches[2]) > 7 then utilities.send_reply(self, msg, 'Invalide PIN!') return end
|
||||
utilities.send_reply(self, msg, twitter_send:get_twitter_access_token(hash, matches[2], oauth_token, oauth_token_secret))
|
||||
return
|
||||
end
|
||||
|
||||
if matches[1] == 'unauth' then
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
end
|
||||
utilities.send_reply(self, msg, twitter_send:reset_twitter_auth(hash), true)
|
||||
return
|
||||
end
|
||||
|
||||
if matches[1] == 'verify' then
|
||||
local text, pp_url = twitter_send:twitter_verify_credentials(oauth_token, oauth_token_secret)
|
||||
local file = download_to_file(pp_url)
|
||||
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
|
||||
utilities.send_reply(self, msg, text)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
if not can_send_tweet(msg) then
|
||||
utilities.send_reply(self, msg, '*Du darfst keine Tweets senden.* Entweder wurdest du noch gar nicht freigeschaltet oder ausgeschlossen.', true)
|
||||
return
|
||||
else
|
||||
utilities.send_reply(self, msg, twitter_send:send_tweet(matches[1], oauth_token, oauth_token_secret, hash), true)
|
||||
return
|
||||
end
|
||||
else
|
||||
utilities.send_reply(self, msg, twitter_send:send_tweet(matches[1], oauth_token, oauth_token_secret, hash), true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return twitter_send
|
@ -1,63 +1,150 @@
|
||||
local weather = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local HTTPS = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function weather:init(config)
|
||||
if not config.owm_api_key then
|
||||
print('Missing config value: owm_api_key.')
|
||||
if not cred_data.forecastio_apikey then
|
||||
print('Missing config value: forecastio_apikey.')
|
||||
print('weather.lua will not be enabled.')
|
||||
return
|
||||
elseif not cred_data.google_apikey then
|
||||
print('Missing config value: google_apikey.')
|
||||
print('weather.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
weather.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('weather', true).table
|
||||
weather.doc = [[```
|
||||
]]..config.cmd_pat..[[weather <location>
|
||||
Returns the current weather conditions for a given location.
|
||||
```]]
|
||||
weather.triggers = {
|
||||
"^/wetter$",
|
||||
"^/wetter (.*)$",
|
||||
"^/w$",
|
||||
"^/w (.*)$"
|
||||
}
|
||||
weather.doc = [[*
|
||||
]]..config.cmd_pat..[[wetter*: Wetter für deinen Wohnort _(/location set [Ort])_
|
||||
*]]..config.cmd_pat..[[wetter* _<Ort>_: Wetter für diesen Ort
|
||||
]]
|
||||
end
|
||||
|
||||
weather.command = 'weather <location>'
|
||||
weather.command = 'wetter'
|
||||
|
||||
function weather:action(msg, config)
|
||||
local BASE_URL = "https://api.forecast.io/forecast"
|
||||
local apikey = cred_data.forecastio_apikey
|
||||
local google_apikey = cred_data.google_apikey
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
if not input then
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, weather.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
function get_city_name(lat, lng)
|
||||
local city = redis:hget('telegram:cache:weather:pretty_names', lat..','..lng)
|
||||
if city then return city end
|
||||
local url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='..lat..','..lng..'&result_type=political&language=de&key='..google_apikey
|
||||
local res, code = HTTPS.request(url)
|
||||
if code ~= 200 then return 'Unbekannte Stadt' end
|
||||
local data = JSON.decode(res).results[1]
|
||||
local city = data.formatted_address
|
||||
print('Setting '..lat..','..lng..' in redis hash telegram:cache:weather:pretty_names to "'..city..'"')
|
||||
redis:hset('telegram:cache:weather:pretty_names', lat..','..lng, city)
|
||||
return city
|
||||
end
|
||||
|
||||
function weather:get_weather(lat, lng)
|
||||
print('Finde Wetter in '..lat..', '..lng)
|
||||
local text = redis:get('telegram:cache:weather:'..lat..','..lng)
|
||||
if text then print('...aus dem Cache') return text end
|
||||
|
||||
local url = BASE_URL..'/'..apikey..'/'..lat..','..lng..'?lang=de&units=si&exclude=minutely,hourly,daily,alerts,flags'
|
||||
|
||||
local response_body = {}
|
||||
local request_constructor = {
|
||||
url = url,
|
||||
method = "GET",
|
||||
sink = ltn12.sink.table(response_body)
|
||||
}
|
||||
local ok, response_code, response_headers, response_status_line = HTTPS.request(request_constructor)
|
||||
if not ok then return nil end
|
||||
local data = JSON.decode(table.concat(response_body))
|
||||
local ttl = string.sub(response_headers["cache-control"], 9)
|
||||
|
||||
|
||||
local weather = data.currently
|
||||
local city = get_city_name(lat, lng)
|
||||
local temperature = string.gsub(round(weather.temperature, 1), "%.", ",")
|
||||
local feelslike = string.gsub(round(weather.apparentTemperature, 1), "%.", ",")
|
||||
local temp = '*Wetter in '..city..':*\n'..temperature..' °C'
|
||||
local conditions = ' | '..weather.summary
|
||||
if weather.icon == 'clear-day' then
|
||||
conditions = conditions..' ☀️'
|
||||
elseif weather.icon == 'clear-night' then
|
||||
conditions = conditions..' 🌙'
|
||||
elseif weather.icon == 'rain' then
|
||||
conditions = conditions..' ☔️'
|
||||
elseif weather.icon == 'snow' then
|
||||
conditions = conditions..' ❄️'
|
||||
elseif weather.icon == 'sleet' then
|
||||
conditions = conditions..' 🌨'
|
||||
elseif weather.icon == 'wind' then
|
||||
conditions = conditions..' 💨'
|
||||
elseif weather.icon == 'fog' then
|
||||
conditions = conditions..' 🌫'
|
||||
elseif weather.icon == 'cloudy' then
|
||||
conditions = conditions..' ☁️☁️'
|
||||
elseif weather.icon == 'partly-cloudy-day' then
|
||||
conditions = conditions..' 🌤'
|
||||
elseif weather.icon == 'partly-cloudy-night' then
|
||||
conditions = conditions..' 🌙☁️'
|
||||
else
|
||||
conditions = conditions..''
|
||||
end
|
||||
local windspeed = ' | 💨 '..string.gsub(round(weather.windSpeed, 1), "%.", ",")..' m/s'
|
||||
|
||||
local text = temp..conditions..windspeed
|
||||
|
||||
if temperature ~= feelslike then
|
||||
text = text..'\n(gefühlt: '..feelslike..' °C)'
|
||||
end
|
||||
|
||||
cache_data('weather', lat..','..lng, text, tonumber(ttl), 'key')
|
||||
return text
|
||||
end
|
||||
|
||||
function weather:action(msg, config, matches)
|
||||
local user_id = msg.from.id
|
||||
|
||||
if matches[1] ~= '/wetter' and matches[1] ~= '/w' then
|
||||
city = matches[1]
|
||||
else
|
||||
local set_location = get_location(user_id)
|
||||
if not set_location then
|
||||
city = 'Berlin, Deutschland'
|
||||
else
|
||||
city = set_location
|
||||
end
|
||||
end
|
||||
|
||||
local coords = utilities.get_coords(input, config)
|
||||
if type(coords) == 'string' then
|
||||
utilities.send_reply(self, msg, coords)
|
||||
return
|
||||
end
|
||||
local lat = redis:hget('telegram:cache:weather:'..string.lower(city), 'lat')
|
||||
local lng = redis:hget('telegram:cache:weather:'..string.lower(city), 'lng')
|
||||
if not lat and not lng then
|
||||
print('Koordinaten nicht eingespeichert, frage Google...')
|
||||
coords = utilities.get_coords(city, config)
|
||||
lat = coords.lat
|
||||
lng = coords.lon
|
||||
end
|
||||
|
||||
local url = 'http://api.openweathermap.org/data/2.5/weather?APPID=' .. config.owm_api_key .. '&lat=' .. coords.lat .. '&lon=' .. coords.lon
|
||||
if not lat and not lng then
|
||||
utilities.send_reply(self, msg, '*Diesen Ort gibt es nicht!*', true)
|
||||
return
|
||||
end
|
||||
|
||||
local jstr, res = HTTP.request(url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local jdat = JSON.decode(jstr)
|
||||
if jdat.cod ~= 200 then
|
||||
utilities.send_reply(self, msg, 'Error: City not found.')
|
||||
return
|
||||
end
|
||||
|
||||
local celsius = string.format('%.2f', jdat.main.temp - 273.15)
|
||||
local fahrenheit = string.format('%.2f', celsius * (9/5) + 32)
|
||||
local output = '`' .. celsius .. '°C | ' .. fahrenheit .. '°F, ' .. jdat.weather[1].description .. '.`'
|
||||
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
redis:hset('telegram:cache:weather:'..string.lower(city), 'lat', lat)
|
||||
redis:hset('telegram:cache:weather:'..string.lower(city), 'lng', lng)
|
||||
|
||||
local text = weather:get_weather(lat, lng)
|
||||
if not text then
|
||||
text = 'Konnte das Wetter von dieser Stadt nicht bekommen.'
|
||||
end
|
||||
utilities.send_reply(self, msg, text, true)
|
||||
end
|
||||
|
||||
return weather
|
||||
|
@ -8,10 +8,10 @@ local utilities = require('otouto.utilities')
|
||||
wikipedia.command = 'wiki <Begriff>'
|
||||
|
||||
function wikipedia:init(config)
|
||||
wikipedia.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('wikipedia', true):t('wiki', true):t('w', true).table
|
||||
wikipedia.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('wikipedia', true):t('wiki', true).table
|
||||
wikipedia.doc = [[*
|
||||
]]..config.cmd_pat..[[wiki* _<Begriff>_: Gibt Wikipedia-Artikel aus
|
||||
Aliase: ]]..config.cmd_pat..[[w, ]]..config.cmd_pat..[[wikipedia]]
|
||||
Alias: ]]..config.cmd_pat..[[wikipedia]]
|
||||
end
|
||||
|
||||
local get_title = function(search)
|
||||
|
@ -142,9 +142,7 @@ function send_youtube_data(data, msg, self, link, sendpic)
|
||||
text = text..'\nACHTUNG, In Deutschland gesperrt!'
|
||||
end
|
||||
local file = download_to_file(image_url)
|
||||
bindings.sendPhoto(self, {chat_id = msg.chat.id, reply_to_message_id = msg.message_id, caption = text }, {photo = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
utilities.send_photo(self, msg.chat.id, file, text, msg.message_id)
|
||||
else
|
||||
utilities.send_reply(self, msg, text, true)
|
||||
end
|
||||
|
@ -17,7 +17,8 @@ function yt_search:init(config)
|
||||
end
|
||||
|
||||
yt_search.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('yt', true):t('youtube', true).table
|
||||
yt_search.doc = [[*]]..config.cmd_pat..[[yt* _<Suchbegriff>_: Sucht nach einem YouTube-Video]]
|
||||
yt_search.doc = [[*
|
||||
]]..config.cmd_pat..[[yt* _<Suchbegriff>_: Sucht nach einem YouTube-Video]]
|
||||
end
|
||||
|
||||
local BASE_URL = 'https://www.googleapis.com/youtube/v3'
|
||||
|
@ -8,6 +8,8 @@ local ltn12 = require('ltn12')
|
||||
local HTTPS = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
local serpent = require("serpent")
|
||||
local bindings = require('otouto.bindings')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
@ -34,6 +36,107 @@ function utilities:send_reply(old_msg, text, use_markdown)
|
||||
parse_mode = use_markdown and 'Markdown' or nil
|
||||
} )
|
||||
end
|
||||
|
||||
-- NOTE: Telegram currently only allows file uploads up to 50 MB
|
||||
-- https://core.telegram.org/bots/api#sendphoto
|
||||
function utilities:send_photo(chat_id, file, text, reply_to_message_id)
|
||||
local output = bindings.request(self, 'sendPhoto', {
|
||||
chat_id = chat_id,
|
||||
caption = text or nil,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
}, {photo = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
return output
|
||||
end
|
||||
|
||||
-- https://core.telegram.org/bots/api#sendaudio
|
||||
function utilities:send_audio(chat_id, file, text, reply_to_message_id, duration, performer, title)
|
||||
local output = bindings.request(self, 'sendAudio', {
|
||||
chat_id = chat_id,
|
||||
caption = text or nil,
|
||||
duration = duration or nil,
|
||||
performer = performer or nil,
|
||||
title = title or nil,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
}, {audio = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
return output
|
||||
end
|
||||
|
||||
-- https://core.telegram.org/bots/api#senddocument
|
||||
function utilities:send_document(chat_id, file, text, reply_to_message_id)
|
||||
local output = bindings.request(self, 'sendDocument', {
|
||||
chat_id = chat_id,
|
||||
caption = text or nil,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
}, {document = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
return output
|
||||
end
|
||||
|
||||
-- https://core.telegram.org/bots/api#sendvideo
|
||||
function utilities:send_video(chat_id, file, text, reply_to_message_id, duration, width, height)
|
||||
local output = bindings.request(self, 'sendVideo', {
|
||||
chat_id = chat_id,
|
||||
caption = text or nil,
|
||||
duration = duration or nil,
|
||||
width = width or nil,
|
||||
height = height or nil,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
}, {video = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
return output
|
||||
end
|
||||
|
||||
-- NOTE: Voice messages are .ogg files encoded with OPUS
|
||||
-- https://core.telegram.org/bots/api#sendvoice
|
||||
function utilities:send_voice(chat_id, file, text, reply_to_message_id, duration)
|
||||
local output = bindings.request(self, 'sendVoice', {
|
||||
chat_id = chat_id,
|
||||
duration = duration or nil,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
}, {voice = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
return output
|
||||
end
|
||||
|
||||
-- https://core.telegram.org/bots/api#sendlocation
|
||||
function utilities:send_location(chat_id, latitude, longitude, reply_to_message_id)
|
||||
return bindings.request(self, 'sendLocation', {
|
||||
chat_id = chat_id,
|
||||
latitude = latitude,
|
||||
longitude = longitude,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
} )
|
||||
end
|
||||
|
||||
-- NOTE: Venue is different from location: it shows information, such as the street adress or
|
||||
-- title of the location with it.
|
||||
-- https://core.telegram.org/bots/api#sendvenue
|
||||
function utilities:send_venue(chat_id, latitude, longitude, reply_to_message_id, title, address)
|
||||
return bindings.request(self, 'sendVenue', {
|
||||
chat_id = chat_id,
|
||||
latitude = latitude,
|
||||
longitude = longitude,
|
||||
title = title,
|
||||
address = address,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
} )
|
||||
end
|
||||
|
||||
-- https://core.telegram.org/bots/api#sendchataction
|
||||
function utilities:send_typing(chat_id, action)
|
||||
return bindings.request(self, 'sendChatAction', {
|
||||
chat_id = chat_id,
|
||||
action = action
|
||||
} )
|
||||
end
|
||||
|
||||
-- get the indexed word in a string
|
||||
function utilities.get_word(s, i)
|
||||
s = s or ''
|
||||
@ -110,6 +213,25 @@ local lc_list = {
|
||||
['!'] = 'ǃ'
|
||||
}
|
||||
|
||||
-- Retruns true if the string is empty
|
||||
function string:isempty()
|
||||
return self == nil or self == ''
|
||||
end
|
||||
|
||||
-- Retruns true if the string is blank
|
||||
function string:isblank()
|
||||
self = self:trim()
|
||||
return self:isempty()
|
||||
end
|
||||
|
||||
function get_name(msg)
|
||||
local name = msg.from.first_name
|
||||
if name == nil then
|
||||
name = msg.from.id
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
-- http://www.lua.org/manual/5.2/manual.html#pdf-io.popen
|
||||
function run_command(str)
|
||||
local cmd = io.popen(str)
|
||||
@ -118,6 +240,12 @@ function run_command(str)
|
||||
return result
|
||||
end
|
||||
|
||||
function convert_timestamp(timestamp, format)
|
||||
local converted_date = run_command('date -d @'..timestamp..' +"'..format..'"')
|
||||
local converted_date = string.gsub(converted_date, '%\n', '')
|
||||
return converted_date
|
||||
end
|
||||
|
||||
function string.starts(String, Start)
|
||||
return Start == string.sub(String,1,string.len(Start))
|
||||
end
|
||||
@ -227,9 +355,9 @@ end
|
||||
-- Gets coordinates for a location. Used by gMaps.lua, time.lua, weather.lua.
|
||||
function utilities.get_coords(input, config)
|
||||
|
||||
local url = 'http://maps.googleapis.com/maps/api/geocode/json?address=' .. URL.escape(input)
|
||||
local url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' .. URL.escape(input)
|
||||
|
||||
local jstr, res = HTTP.request(url)
|
||||
local jstr, res = HTTPS.request(url)
|
||||
if res ~= 200 then
|
||||
return config.errors.connection
|
||||
end
|
||||
@ -461,6 +589,62 @@ utilities.char = {
|
||||
em_dash = '—'
|
||||
}
|
||||
|
||||
-- Returns a table with matches or nil
|
||||
--function match_pattern(pattern, text, lower_case)
|
||||
function match_pattern(pattern, text)
|
||||
if text then
|
||||
local matches = { string.match(text, pattern) }
|
||||
if next(matches) then
|
||||
return matches
|
||||
end
|
||||
end
|
||||
-- nil
|
||||
end
|
||||
|
||||
function post_petition(url, arguments, headers)
|
||||
local url, h = string.gsub(url, "http://", "")
|
||||
local url, hs = string.gsub(url, "https://", "")
|
||||
local post_prot = "http"
|
||||
if hs == 1 then
|
||||
post_prot = "https"
|
||||
end
|
||||
local response_body = {}
|
||||
local request_constructor = {
|
||||
url = post_prot..'://'..url,
|
||||
method = "POST",
|
||||
sink = ltn12.sink.table(response_body),
|
||||
headers = headers or {},
|
||||
redirect = false
|
||||
}
|
||||
|
||||
local source = arguments
|
||||
if type(arguments) == "table" then
|
||||
local source = helpers.url_encode_arguments(arguments)
|
||||
end
|
||||
|
||||
if not headers then
|
||||
request_constructor.headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF8"
|
||||
request_constructor.headers["X-Accept"] = "application/json"
|
||||
request_constructor.headers["Accept"] = "application/json"
|
||||
end
|
||||
request_constructor.headers["Content-Length"] = tostring(#source)
|
||||
request_constructor.source = ltn12.source.string(source)
|
||||
|
||||
if post_prot == "http" then
|
||||
ok, response_code, response_headers, response_status_line = http.request(request_constructor)
|
||||
else
|
||||
ok, response_code, response_headers, response_status_line = https.request(request_constructor)
|
||||
end
|
||||
|
||||
if not ok then
|
||||
return nil
|
||||
end
|
||||
|
||||
response_body = JSON.decode(table.concat(response_body))
|
||||
|
||||
return response_body, response_headers
|
||||
end
|
||||
|
||||
function get_redis_hash(msg, var)
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
return 'chat:'..msg.chat.id..':'..var
|
||||
@ -481,6 +665,14 @@ function tablelength(T)
|
||||
return count
|
||||
end
|
||||
|
||||
function round(num, idp)
|
||||
if idp and idp>0 then
|
||||
local mult = 10^idp
|
||||
return math.floor(num * mult + 0.5) / mult
|
||||
end
|
||||
return math.floor(num + 0.5)
|
||||
end
|
||||
|
||||
function comma_value(amount)
|
||||
local formatted = amount
|
||||
while true do
|
||||
@ -497,6 +689,16 @@ function string.ends(str, fin)
|
||||
return fin=='' or string.sub(str,-string.len(fin)) == fin
|
||||
end
|
||||
|
||||
function get_location(user_id)
|
||||
local hash = 'user:'..user_id
|
||||
local set_location = redis:hget(hash, 'location')
|
||||
if set_location == 'false' or set_location == nil then
|
||||
return false
|
||||
else
|
||||
return set_location
|
||||
end
|
||||
end
|
||||
|
||||
function cache_data(plugin, query, data, timeout, typ)
|
||||
-- How to: cache_data(pluginname, query_name, data_to_cache, expire_in_seconds)
|
||||
local hash = 'telegram:cache:'..plugin..':'..query
|
||||
@ -619,4 +821,14 @@ function unescape(str)
|
||||
return str
|
||||
end
|
||||
|
||||
function url_encode(str)
|
||||
if (str) then
|
||||
str = string.gsub (str, "\n", "\r\n")
|
||||
str = string.gsub (str, "([^%w %-%_%.%~])",
|
||||
function (c) return string.format ("%%%02X", string.byte(c)) end)
|
||||
str = string.gsub (str, " ", "+")
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
return utilities
|
||||
|
Reference in New Issue
Block a user