local gh_feed = {} gh_feed.command = 'gh ' function gh_feed:init(config) if not cred_data.github_token then print('Fehlender Key: github_token.') print('github_feed.lua wird nicht aktiviert.') return elseif not cred_data.github_username then print('Fehlender Key: github_username.') print('github_feed.lua wird nicht aktiviert.') return end gh_feed.triggers = { "^/(gh) @(.*)$", "^/gh$", "^/gh (sub) ([A-Za-z0-9-_-.-._.]+/[A-Za-z0-9-_-.-._.]+) @(.*)$", "^/gh (sub) ([A-Za-z0-9-_-.-._.]+/[A-Za-z0-9-_-.-._.]+)$", "^/gh (del) (%d+) @(.*)$", "^/gh (del) (%d+)$", "^/gh (del)", "^/gh (sync)$" } gh_feed.doc = [[* ]]..config.cmd_pat..[[gh* _@[Kanalname]_: GitHub-Abonnements anzeigen *]]..config.cmd_pat..[[gh* _sub_ __ _@[Kanalname]_: Diese Repo abonnieren *]]..config.cmd_pat..[[gh* _del_ _<#>_ _@[Kanalname]_: Diese Repo deabonnieren *]]..config.cmd_pat..[[gh* _sync_: Repos syncen (nur Superuser) Der Kanalname ist optional]] end local token = cred_data.github_token -- get a token here: https://github.com/settings/tokens/new (you don't need any scopes) local BASE_URL = 'https://api.github.com/repos' function gh_feed_get_base_redis(id, option, extra) local ex = '' if option ~= nil then ex = ex .. ':' .. option if extra ~= nil then ex = ex .. ':' .. extra end end return 'github:' .. id .. ex end function gh_feed_check_modified(repo, cur_etag, last_date) local url = BASE_URL..'/'..repo local request_constructor = { url = url, method = "HEAD", redirect = false, sink = ltn12.sink.null(), headers = { Authorization = 'token '..token, ["If-None-Match"] = cur_etag } } local ok, response_code = https.request(request_constructor) if not ok then return nil end if response_code == 304 then return true end local url = BASE_URL..'/'..repo..'/commits?since='..last_date local response_body = {} local request_constructor = { url = url, method = "GET", sink = ltn12.sink.table(response_body), headers = { Authorization = 'token '..token } } local ok, response_code, response_headers = https.request(request_constructor) if not response_headers then return nil end local data = json.decode(table.concat(response_body)) return false, data, response_headers.etag end function gh_feed:check_repo(repo) local url = BASE_URL..'/'..repo local response_body = {} local request_constructor = { url = url, method = "GET", sink = ltn12.sink.table(response_body), headers = { Authorization = 'token '..token } } local ok, response_code, response_headers = https.request(request_constructor) if not ok then return nil end return json.decode(table.concat(response_body)), response_headers.etag end function gh_feed:subscribe(id, repo) local lasthash = gh_feed_get_base_redis(repo, "last_commit") local lastetag = gh_feed_get_base_redis(repo, "etag") local lastdate = gh_feed_get_base_redis(repo, "date") local lhash = gh_feed_get_base_redis(repo, "subs") local uhash = gh_feed_get_base_redis(id) if redis:sismember(uhash, repo) then return "Du hast `"..repo.."` bereits abonniert." end local data, etag = gh_feed:check_repo(repo) if not data or not data.full_name then return 'Diese Repo gibt es nicht!' end if not etag then return 'Ein Fehler ist aufgetreten.' end local last_commit = "" local pushed_at = data.pushed_at local name = data.full_name redis:set(lasthash, last_commit) redis:set(lastdate, pushed_at) redis:set(lastetag, etag) redis:sadd(lhash, id) redis:sadd(uhash, repo) return "_"..utilities.md_escape(name) .."_ abonniert!" end function gh_feed:unsubscribe(id, n) if #n > 3 then return "Du kannst nicht mehr als drei Repos abonnieren!" end n = tonumber(n) local uhash = gh_feed_get_base_redis(id) local subs = redis:smembers(uhash) if n < 1 or n > #subs then return "Abonnement-ID zu hoch!" end local sub = subs[n] local lhash = gh_feed_get_base_redis(sub, "subs") redis:srem(uhash, sub) redis:srem(lhash, id) local left = redis:smembers(lhash) if #left < 1 then -- no one subscribed, remove it local lastetag = gh_feed_get_base_redis(sub, "etag") local lastdate = gh_feed_get_base_redis(sub, "date") local lasthash = gh_feed_get_base_redis(sub, "last_commit") redis:del(lastetag) redis:del(lasthash) redis:del(lastdate) end return "Du hast `"..utilities.md_escape(sub).."` deabonniert." end function gh_feed:print_subs(id, chat_name) local uhash = gh_feed_get_base_redis(id) local subs = redis:smembers(uhash) if not subs[1] then return 'Keine GitHub-Repos abonniert!' end local keyboard = '{"keyboard":[[' local keyboard_buttons = '' local text = '*'..chat_name..'* hat abonniert:\n---------\n' for k,v in pairs(subs) do text = text .. k .. ") ["..v.."](https://github.com/"..v..')\n' if k == #subs then keyboard_buttons = keyboard_buttons..'{"text":"/gh del '..k..'"}' break; end keyboard_buttons = keyboard_buttons..'{"text":"/gh del '..k..'"},' end local keyboard = keyboard..keyboard_buttons..']], "one_time_keyboard":true, "selective":true, "resize_keyboard":true}' return text, keyboard end function gh_feed:action(msg, config, matches) local id = msg.from.id if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then id = msg.chat.id end -- For channels if matches[1] == 'sub' and matches[2] and matches[3] then if not is_sudo(msg, config) then utilities.send_reply(msg, config.errors.sudo, true) return end local id = '@'..matches[3] local result = utilities.get_chat_info(id) if not result then utilities.send_reply(msg, 'Diesen Kanal gibt es nicht!') return end local output = gh_feed:subscribe(id, matches[2]) utilities.send_reply(msg, output, true) return elseif matches[1] == 'del' and matches[2] and matches[3] then if not is_sudo(msg, config) then utilities.send_reply(msg, config.errors.sudo, true) return end local id = '@'..matches[3] local result = utilities.get_chat_info(id) if not result then utilities.send_reply(msg, 'Diesen Kanal gibt es nicht!') return end local output = gh_feed:unsubscribe(id, matches[2]) utilities.send_reply(msg, output, true) return elseif matches[1] == 'gh' and matches[2] then local id = '@'..matches[2] local result = utilities.get_chat_info(id) if not result then utilities.send_reply(msg, 'Diesen Kanal gibt es nicht!') return end local chat_name = result.result.title local output = gh_feed:print_subs(id, chat_name) utilities.send_reply(msg, output, true) return end if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then chat_name = msg.chat.title else chat_name = msg.chat.first_name end if matches[1] == 'sub' and matches[2] then if not is_sudo(msg, config) then utilities.send_reply(msg, config.errors.sudo, true) return end local output = gh_feed:subscribe(id, matches[2]) utilities.send_reply(msg, output, true) return elseif matches[1] == 'del' and matches[2] then if not is_sudo(msg, config) then utilities.send_reply(msg, config.errors.sudo, true) return end local output = gh_feed:unsubscribe(id, matches[2]) utilities.send_reply(msg, output, true, '{"hide_keyboard":true}') return elseif matches[1] == 'del' and not matches[2] then local list_subs, keyboard = gh_feed:print_subs(id, chat_name) utilities.send_reply(msg, list_subs, true, keyboard) return elseif matches[1] == 'sync' then if not is_sudo(msg, config) then utilities.send_reply(msg, config.errors.sudo, true) return end gh_feed:cron() return end local output = gh_feed:print_subs(id, chat_name) utilities.send_reply(msg, output, true) return end function gh_feed:cron() local keys = redis:keys(gh_feed_get_base_redis("*", "subs")) for k,v in pairs(keys) do local repo = string.match(v, "github:(.+):subs") print('GitHub: '..repo) local cur_etag = redis:get(gh_feed_get_base_redis(repo, "etag")) local last_date = redis:get(gh_feed_get_base_redis(repo, "date")) local was_not_modified, data, last_etag = gh_feed_check_modified(repo, cur_etag, last_date) if not was_not_modified then if not data or not last_etag then return end -- When there are new commits local last_commit = redis:get(gh_feed_get_base_redis(repo, "last_commit")) text = '' for n in ipairs(data) do if data[n].sha ~= last_commit then local sha = data[n].sha local author = data[n].commit.author.name local message = utilities.md_escape(data[n].commit.message) local link = data[n].html_url text = text..'\n#GitHub: `'..repo..'@'..sha..'` von *'..author..'*:\n'..message..'\n[GitHub aufrufen]('..link..')\n' end end if text ~= '' then local last_commit = data[1].sha local last_date = data[1].commit.author.date redis:set(gh_feed_get_base_redis(repo, "last_commit"), last_commit) redis:set(gh_feed_get_base_redis(repo, "etag"), last_etag) redis:set(gh_feed_get_base_redis(repo, "date"), last_date) for k2, receiver in pairs(redis:smembers(v)) do utilities.send_message(receiver, text, true, nil, true) end end end end end return gh_feed