From f5b96a8202f1732bfdf3631d6a581ba263825dc9 Mon Sep 17 00:00:00 2001 From: dnns01 <git@dnns01.de> Date: Sat, 15 May 2021 21:35:41 +0200 Subject: [PATCH] Add github integration to add issues from discord, similar to "Decky" --- .env.template | 7 +++- .gitignore | 1 + fernuni_bot.py | 6 +-- github.py | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 github.py diff --git a/.env.template b/.env.template index 32adcbc..6577977 100644 --- a/.env.template +++ b/.env.template @@ -2,6 +2,9 @@ DISCORD_TOKEN=<Bot Token> DISCORD_GUILD=<ID of Guild, this Bot should be used at> DISCORD_ACTIVITY=<What should be shown, Bot is playing right now> +DISCORD_GITHUB_USER=<Github username used to create issues> +DISCORD_GITHUB_TOKEN=<Github personal access token, can be created in Settings > Developer settings > Personal access tokens (repo scope neccessary)> +DISCORD_GITHUB_ISSUE_URL=<URL of Github API to create Issues in a repo> # IDs DISCORD_OWNER=<ID of Server owner> @@ -28,6 +31,8 @@ DISCORD_LEARNINGGROUPS_OPEN=<ID of Channel category for open learning groups> DISCORD_LEARNINGGROUPS_CLOSE=<ID of Channel category for closed learning groups> DISCORD_LEARNINGGROUPS_REQUEST=<ID of Channel category for learning group requests> DISCORD_LEARNINGGROUPS_INFO=<ID of Channel category for open learning groups> +DISCORD_IDEE_CHANNEL=<ID of Channel, where bot ideas can be submitted> +DISCORD_IDEE_EMOJI=<ID of Idee Emoji, used for reactions> # JSON Files DISCORD_ROLES_FILE=<File name for roles JSON file> @@ -40,7 +45,7 @@ DISCORD_LEARNINGGROUPS_COURSE_FILE=<File name for leaarning groups courses JSON # Misc DISCORD_DATE_TIME_FORMAT=<Date and time format used for commands like %d.%m.%Y %H:%M> - +DISCORD_IDEE_REACT_QTY=<Amount of reactions to a submitted idea, neccessary to create a github issue (amount is including botys own reaction)> diff --git a/.gitignore b/.gitignore index 3a140b1..03d424b 100644 --- a/.gitignore +++ b/.gitignore @@ -124,3 +124,4 @@ GitHub.sublime-settings /easter.json /learninggroups.json /courses.json +/github.json diff --git a/fernuni_bot.py b/fernuni_bot.py index 2a62c44..98395c7 100644 --- a/fernuni_bot.py +++ b/fernuni_bot.py @@ -1,4 +1,3 @@ -import json import os import discord @@ -6,11 +5,12 @@ from discord.ext import commands from dotenv import load_dotenv # from welcome_cog import WelcomeCog -import utils from appointments_cog import AppointmentsCog from armin import Armin from christmas_cog import ChristmasCog from easter_cog import EasterCog +from github import Github +from help.help import Help from learninggroups import LearningGroups from links_cog import LinksCog from news_cog import NewsCog @@ -21,7 +21,6 @@ from text_commands_cog import TextCommandsCog # from change_log import ChangeLogCog from voice_cog import VoiceCog from welcome_cog import WelcomeCog -from help.help import Help # .env file is necessary in the same directory, that contains several strings. load_dotenv() @@ -53,6 +52,7 @@ bot.add_cog(EasterCog(bot)) bot.add_cog(Armin(bot)) bot.add_cog(LearningGroups(bot)) bot.add_cog(Help(bot)) +bot.add_cog(Github(bot)) def get_reaction(reactions): diff --git a/github.py b/github.py new file mode 100644 index 0000000..79e3f72 --- /dev/null +++ b/github.py @@ -0,0 +1,99 @@ +import base64 +import json +import os + +from aiohttp import ClientSession +from discord.ext import commands + +import utils +from help.help import help, handle_error, help_category + + +@help_category("github", "Github", "Github Integration in Discord.") +class Github(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.github_file = "github.json" + self.data = self.load() + + def load(self): + github_file = open(self.github_file, 'r') + return json.load(github_file) + + def save(self): + github_file = open(self.github_file, 'w') + json.dump(self.data, github_file) + + @help( + category="github", + syntax="!idee <text>", + brief="Stellt eine Idee für Boty zur Abstimmung.", + parameters={ + "text": "Text der Idee.", + }, + description="Mit diesem Kommando kannst du eine Idee für Boty zur Abstimmung einreichen. Sobald genug " + "Reaktionen von anderen Mitgliedern vorhanden sind, wird aus deiner Idee ein Issue in Github " + "erstellt, und sobald möglich kümmert sich jemand darum." + ) + @commands.command(name="idee") + async def cmd_idee(self, ctx): + if ctx.channel.id == int(os.getenv("DISCORD_IDEE_CHANNEL")): + self.data[str(ctx.message.id)] = {"created": False} + await ctx.message.add_reaction(self.bot.get_emoji(int(os.getenv("DISCORD_IDEE_EMOJI")))) + self.save() + + @help( + category="github", + syntax="!card <text>", + brief="Erstellt einen Issue in Github.", + parameters={ + "text": "Text der Idee.", + }, + description="Mit diesem Kommando kannst du einen Issue in Github anlegen.", + mod=True + ) + @commands.command(name="card") + @commands.check(utils.is_mod) + async def cmd_card(self, ctx): + self.data[str(ctx.message.id)] = {"created": False} + await self.create_issue(self.data[str(ctx.message.id)], ctx.message) + self.save() + + @commands.Cog.listener() + async def on_raw_reaction_add(self, payload): + if payload.member == self.bot.user: + return + + if idea := self.data.get(str(payload.message_id)): + if payload.emoji.id == int(os.getenv("DISCORD_IDEE_EMOJI")): + channel = await self.bot.fetch_channel(payload.channel_id) + message = await channel.fetch_message(payload.message_id) + for reaction in message.reactions: + if reaction.emoji.id == int(os.getenv("DISCORD_IDEE_EMOJI")): + if reaction.count >= int(os.getenv("DISCORD_IDEE_REACT_QTY")) and not idea.get("created"): + await self.create_issue(idea, message) + + self.save() + + async def cog_command_error(self, ctx, error): + await handle_error(ctx, error) + + async def create_issue(self, idea, message): + async with ClientSession() as session: + auth = base64.b64encode( + f'{os.getenv("DISCORD_GITHUB_USER")}:{os.getenv("DISCORD_GITHUB_TOKEN")}'.encode('utf-8')).decode( + "utf-8") + headers = {"Authorization": f"Basic {auth}", "Content-Type": "application/json"} + + async with session.post(os.getenv("DISCORD_GITHUB_ISSUE_URL"), + headers=headers, + json={'title': message.content[6:]}) as r: + if r.status == 201: + js = await r.json() + + idea["created"] = True + idea["number"] = js["number"] + idea["html_url"] = js["html_url"] + + await message.reply( + f"Danke <@!{message.author.id}> für deinen Vorschlag. Ich habe für dich gerade folgenden Issue in Github erstellt: {idea['html_url']}") -- GitLab