Skip to content

Commit 630e2c5

Browse files
feat(github): add github integration (#368)
1 parent 830ca7b commit 630e2c5

36 files changed

+2136
-397
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,16 @@ jobs:
4141
- name: Test with pytest
4242
id: test
4343
env:
44+
CI_EVENT_ID: ${{ github.event.number || github.sha }}
4445
GITHUB_PYTEST: "true"
46+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4547
DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_TEST_BOT_TOKEN }}
46-
DISCORD_WEBHOOK: ${{ secrets.DISCORD_TEST_BOT_WEBHOOK }}
48+
DISCORD_GITHUB_STATUS_CHANNEL_ID: ${{ vars.DISCORD_GITHUB_STATUS_CHANNEL_ID }}
49+
DISCORD_REDDIT_CHANNEL_ID: ${{ vars.DISCORD_REDDIT_CHANNEL_ID }}
50+
DISCORD_SPONSORS_CHANNEL_ID: ${{ vars.DISCORD_SPONSORS_CHANNEL_ID }}
4751
GRAVATAR_EMAIL: ${{ secrets.GRAVATAR_EMAIL }}
52+
IGDB_CLIENT_ID: ${{ secrets.TWITCH_CLIENT_ID }}
53+
IGDB_CLIENT_SECRET: ${{ secrets.TWITCH_CLIENT_SECRET }}
4854
PRAW_CLIENT_ID: ${{ secrets.REDDIT_CLIENT_ID }}
4955
PRAW_CLIENT_SECRET: ${{ secrets.REDDIT_CLIENT_SECRET }}
5056
REDDIT_USERNAME: ${{ secrets.REDDIT_USERNAME }}

Dockerfile

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,50 @@ ENV COMMIT=${COMMIT}
1717
ARG DAILY_TASKS=true
1818
ARG DAILY_RELEASES=true
1919
ARG DAILY_TASKS_UTC_HOUR=12
20+
ARG DISCORD_GITHUB_STATUS_CHANNEL_ID
21+
ARG DISCORD_REDDIT_CHANNEL_ID
22+
ARG DISCORD_SPONSORS_CHANNEL_ID
2023

2124
# Secret config
22-
ARG DISCORD_BOT_TOKEN
2325
ARG DAILY_CHANNEL_ID
26+
ARG DISCORD_BOT_TOKEN
27+
ARG DISCORD_CLIENT_ID
28+
ARG DISCORD_CLIENT_SECRET
29+
ARG DISCORD_REDIRECT_URI
30+
ARG GITHUB_CLIENT_ID
31+
ARG GITHUB_CLIENT_SECRET
32+
ARG GITHUB_REDIRECT_URI
33+
ARG GITHUB_WEBHOOK_SECRET_KEY
2434
ARG GRAVATAR_EMAIL
2535
ARG IGDB_CLIENT_ID
2636
ARG IGDB_CLIENT_SECRET
2737
ARG PRAW_CLIENT_ID
2838
ARG PRAW_CLIENT_SECRET
2939
ARG PRAW_SUBREDDIT
30-
ARG DISCORD_WEBHOOK
31-
ARG GRAVATAR_EMAIL
32-
ARG REDIRECT_URI
3340

3441
# Environment variables
3542
ENV DAILY_TASKS=$DAILY_TASKS
3643
ENV DAILY_RELEASES=$DAILY_RELEASES
3744
ENV DAILY_CHANNEL_ID=$DAILY_CHANNEL_ID
3845
ENV DAILY_TASKS_UTC_HOUR=$DAILY_TASKS_UTC_HOUR
3946
ENV DISCORD_BOT_TOKEN=$DISCORD_BOT_TOKEN
47+
ENV DISCORD_CLIENT_ID=$DISCORD_CLIENT_ID
48+
ENV DISCORD_CLIENT_SECRET=$DISCORD_CLIENT_SECRET
49+
ENV DISCORD_GITHUB_STATUS_CHANNEL_ID=$DISCORD_GITHUB_STATUS_CHANNEL_ID
50+
ENV DISCORD_REDDIT_CHANNEL_ID=$DISCORD_REDDIT_CHANNEL_ID
51+
ENV DISCORD_REDIRECT_URI=$DISCORD_REDIRECT_URI
52+
ENV DISCORD_SPONSORS_CHANNEL_ID=$DISCORD_SPONSORS_CHANNEL_ID
53+
ENV GITHUB_CLIENT_ID=$GITHUB_CLIENT_ID
54+
ENV GITHUB_CLIENT_SECRET=$GITHUB_CLIENT_SECRET
55+
ENV GITHUB_REDIRECT_URI=$GITHUB_REDIRECT_URI
56+
ENV GITHUB_WEBHOOK_SECRET_KEY=$GITHUB_WEBHOOK_SECRET_KEY
4057
ENV GRAVATAR_EMAIL=$GRAVATAR_EMAIL
4158
ENV IGDB_CLIENT_ID=$IGDB_CLIENT_ID
4259
ENV IGDB_CLIENT_SECRET=$IGDB_CLIENT_SECRET
4360
ENV PRAW_CLIENT_ID=$PRAW_CLIENT_ID
4461
ENV PRAW_CLIENT_SECRET=$PRAW_CLIENT_SECRET
4562
ENV PRAW_SUBREDDIT=$PRAW_SUBREDDIT
4663
ENV DISCORD_WEBHOOK=$DISCORD_WEBHOOK
47-
ENV GRAVATAR_EMAIL=$GRAVATAR_EMAIL
48-
ENV REDIRECT_URI=$REDIRECT_URI
4964

5065
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
5166
# install dependencies
@@ -69,7 +84,7 @@ RUN <<_SETUP
6984
set -e
7085

7186
# replace the version in the code
72-
sed -i "s/version = '0.0.0'/version = '${BUILD_VERSION}'/g" src/common.py
87+
sed -i "s/version = '0.0.0'/version = '${BUILD_VERSION}'/g" src/common/common.py
7388

7489
# install dependencies
7590
python -m pip install --no-cache-dir -r requirements.txt

README.md

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
[![GitHub Workflow Status (CI)](https://img.shields.io/github/actions/workflow/status/lizardbyte/support-bot/ci.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge)](https://github.com/LizardByte/support-bot/actions/workflows/ci.yml?query=branch%3Amaster)
33
[![Codecov](https://img.shields.io/codecov/c/gh/LizardByte/support-bot.svg?token=900Q93P1DE&style=for-the-badge&logo=codecov&label=codecov)](https://app.codecov.io/gh/LizardByte/support-bot)
44

5-
Support bot written in python to help manage LizardByte communities. The current focus is discord and reddit, but other
6-
platforms such as GitHub discussions/issues could be added.
5+
Support bot written in python to help manage LizardByte communities. The current focus is Discord and Reddit, but other
6+
platforms such as GitHub discussions/issues might be added in the future.
77

88

99
## Overview
@@ -24,45 +24,52 @@ platforms such as GitHub discussions/issues could be added.
2424
* Presence Intent
2525
* Server Members Intent
2626
* Copy the `Token`
27-
* Add the following as environment variables or in a `.env` file (use `sample.env` as an example).
28-
:exclamation: if using Docker these can be arguments.
29-
:warning: Never publicly expose your tokens, secrets, or ids.
30-
31-
| variable | required | default | description |
32-
|-------------------------|----------|------------------------------------------------------|---------------------------------------------------------------|
33-
| DISCORD_BOT_TOKEN | True | `None` | Token from Bot page on discord developer portal. |
34-
| DAILY_TASKS | False | `true` | Daily tasks on or off. |
35-
| DAILY_RELEASES | False | `true` | Send a message for each game released on this day in history. |
36-
| DAILY_CHANNEL_ID | False | `None` | Required if daily_tasks is enabled. |
37-
| DAILY_TASKS_UTC_HOUR | False | `12` | The hour to run daily tasks. |
38-
| GRAVATAR_EMAIL | False | `None` | Gravatar email address for bot avatar. |
39-
| IGDB_CLIENT_ID | False | `None` | Required if daily_releases is enabled. |
40-
| IGDB_CLIENT_SECRET | False | `None` | Required if daily_releases is enabled. |
41-
| SUPPORT_COMMANDS_REPO | False | `https://github.com/LizardByte/support-bot-commands` | Repository for support commands. |
42-
| SUPPORT_COMMANDS_BRANCH | False | `master` | Branch for support commands. |
43-
44-
* Running bot:
45-
* `python -m src`
46-
* Invite bot to server:
47-
* `https://discord.com/api/oauth2/authorize?client_id=<the client id of the bot>&permissions=8&scope=bot%20applications.commands`
48-
4927

5028
### Reddit
5129

5230
* Set up an application at [reddit apps](https://www.reddit.com/prefs/apps/).
5331
* The redirect uri should be https://localhost:8080
5432
* Take note of the `client_id` and `client_secret`
55-
* Enter the following as environment variables
56-
57-
| Parameter | Required | Default | Description |
58-
|--------------------|----------|---------|-------------------------------------------------------------------------|
59-
| PRAW_CLIENT_ID | True | None | `client_id` from reddit app setup page. |
60-
| PRAW_CLIENT_SECRET | True | None | `client_secret` from reddit app setup page. |
61-
| PRAW_SUBREDDIT | True | None | Subreddit to monitor (reddit user should be moderator of the subreddit) |
62-
| DISCORD_WEBHOOK | False | None | URL of webhook to send discord notifications to |
63-
| GRAVATAR_EMAIL | False | None | Gravatar email address to get avatar from |
64-
| REDDIT_USERNAME | True | None | Reddit username |
65-
* | REDDIT_PASSWORD | True | None | Reddit password |
66-
67-
* Running bot:
68-
* `python -m src`
33+
34+
### Environment Variables
35+
36+
* Add the following as environment variables or in a `.env` file (use `sample.env` as an example).
37+
:exclamation: if using Docker these can be arguments.
38+
:warning: Never publicly expose your tokens, secrets, or ids.
39+
40+
| variable | required | default | description |
41+
|----------------------------------|----------|------------------------------------------------------|-------------------------------------------------------------------------|
42+
| DAILY_TASKS | False | `true` | Daily tasks on or off. |
43+
| DAILY_RELEASES | False | `true` | Send a message for each game released on this day in history. |
44+
| DAILY_CHANNEL_ID | False | `None` | Required if daily_tasks is enabled. |
45+
| DAILY_TASKS_UTC_HOUR | False | `12` | The hour to run daily tasks. |
46+
| DISCORD_BOT_TOKEN | True | `None` | Token from Bot page on discord developer portal. |
47+
| DISCORD_CLIENT_ID | True | `None` | Discord OAuth2 client id. |
48+
| DISCORD_CLIENT_SECRET | True | `None` | Discord OAuth2 client secret. |
49+
| DISCORD_GITHUB_STATUS_CHANNEL_ID | True | `None` | Channel ID to send GitHub status updates to. |
50+
| DISCORD_REDDIT_CHANNEL_ID | True | `None` | Channel ID to send Reddit post updates to. |
51+
| DISCORD_REDIRECT_URI | False | `https://localhost:8080/discord/callback` | The redirect uri for OAuth2. Must be publicly accessible. |
52+
| DISCORD_SPONSORS_CHANNEL_ID | True | `None` | Channel ID to send sponsorship updates to. |
53+
| GITHUB_CLIENT_ID | True | `None` | GitHub OAuth2 client id. |
54+
| GITHUB_CLIENT_SECRET | True | `None` | GitHub OAuth2 client secret. |
55+
| GITHUB_REDIRECT_URI | False | `https://localhost:8080/github/callback` | The redirect uri for OAuth2. Must be publicly accessible. |
56+
| GITHUB_WEBHOOK_SECRET_KEY | True | `None` | A secret value to ensure webhooks are from trusted sources. |
57+
| GRAVATAR_EMAIL | False | `None` | Gravatar email address for bot avatar. |
58+
| IGDB_CLIENT_ID | False | `None` | Required if daily_releases is enabled. |
59+
| IGDB_CLIENT_SECRET | False | `None` | Required if daily_releases is enabled. |
60+
| PRAW_CLIENT_ID | True | None | `client_id` from reddit app setup page. |
61+
| PRAW_CLIENT_SECRET | True | None | `client_secret` from reddit app setup page. |
62+
| PRAW_SUBREDDIT | True | None | Subreddit to monitor (reddit user should be moderator of the subreddit) |
63+
| REDDIT_USERNAME | True | None | Reddit username |
64+
| REDDIT_PASSWORD | True | None | Reddit password |
65+
| SUPPORT_COMMANDS_REPO | False | `https://github.com/LizardByte/support-bot-commands` | Repository for support commands. |
66+
| SUPPORT_COMMANDS_BRANCH | False | `master` | Branch for support commands. |
67+
68+
### Start
69+
70+
```bash
71+
python -m src
72+
```
73+
74+
* Invite bot to server:
75+
* `https://discord.com/api/oauth2/authorize?client_id=<the client id of the bot>&permissions=8&scope=bot%20applications.commands`

assets/favicon.ico

112 KB
Binary file not shown.

requirements-dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ betamax-serializers==0.2.1
33
pytest==8.3.4
44
pytest-asyncio==0.24.0
55
pytest-cov==6.0.0
6+
pytest-mock==3.14.0

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
cryptography==43.0.3
12
Flask==3.1.0
23
GitPython==3.1.43
34
igdb-api-v4==0.3.3
@@ -7,3 +8,4 @@ praw==7.8.1
78
py-cord==2.6.1
89
python-dotenv==1.0.1
910
requests==2.32.3
11+
requests-oauthlib==2.0.0

sample.env

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,27 @@ DAILY_TASKS=true
33
DAILY_RELEASES=true
44
DAILY_CHANNEL_ID=
55
DAILY_TASKS_UTC_HOUR=12
6+
SUPPORT_COMMANDS_BRANCH=master
67

78
# Secret settings
89
DISCORD_BOT_TOKEN=
910
GRAVATAR_EMAIL=
11+
12+
# discord/github oauth2
13+
DISCORD_CLIENT_ID=
14+
DISCORD_CLIENT_SECRET=
15+
DISCORD_REDIRECT_URI=
16+
GITHUB_CLIENT_ID=
17+
GITHUB_CLIENT_SECRET=
18+
GITHUB_REDIRECT_URI=
19+
20+
# igdb
1021
IGDB_CLIENT_ID=
1122
IGDB_CLIENT_SECRET=
12-
READTHEDOCS_TOKEN=
1323

1424
# reddit bot
1525
PRAW_CLIENT_ID=
1626
PRAW_CLIENT_SECRET=
1727
PRAW_SUBREDDIT=AskReddit
18-
DISCORD_WEBHOOK=
19-
GRAVATAR_EMAIL=
20-
REDIRECT_URI=
28+
REDDIT_USERNAME=
29+
REDDIT_PASSWORD=

src/__main__.py

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,33 @@
11
# standard imports
2-
import os
32
import time
43

54
# development imports
65
from dotenv import load_dotenv
76
load_dotenv(override=False) # environment secrets take priority over .env file
87

9-
# local imports
10-
if True: # hack for flake8
11-
from src.discord import bot as d_bot
12-
from src import keep_alive
13-
from src.reddit import bot as r_bot
8+
# local imports, import after env loaded
9+
from src.common import globals # noqa: E402
10+
from src.discord import bot as d_bot # noqa: E402
11+
from src.common import webapp # noqa: E402
12+
from src.reddit import bot as r_bot # noqa: E402
1413

1514

1615
def main():
17-
# to run in replit
18-
try:
19-
os.environ['REPL_SLUG']
20-
except KeyError:
21-
pass # not running in replit
22-
else:
23-
keep_alive.keep_alive() # Start the web server
16+
webapp.start() # Start the web server
2417

25-
discord_bot = d_bot.Bot()
26-
discord_bot.start_threaded() # Start the discord bot
18+
globals.DISCORD_BOT = d_bot.Bot()
19+
globals.DISCORD_BOT.start_threaded() # Start the discord bot
2720

28-
reddit_bot = r_bot.Bot()
29-
reddit_bot.start_threaded() # Start the reddit bot
21+
globals.REDDIT_BOT = r_bot.Bot()
22+
globals.REDDIT_BOT.start_threaded() # Start the reddit bot
3023

3124
try:
32-
while discord_bot.bot_thread.is_alive() or reddit_bot.bot_thread.is_alive():
25+
while globals.DISCORD_BOT.bot_thread.is_alive() or globals.REDDIT_BOT.bot_thread.is_alive():
3326
time.sleep(0.5)
3427
except KeyboardInterrupt:
3528
print("Keyboard Interrupt Detected")
36-
discord_bot.stop()
37-
reddit_bot.stop()
29+
globals.DISCORD_BOT.stop()
30+
globals.REDDIT_BOT.stop()
3831

3932

4033
if __name__ == '__main__': # pragma: no cover

src/common/__init__.py

Whitespace-only changes.

src/common.py renamed to src/common/common.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@
77
import requests
88

99

10+
colors = {
11+
'black': 0x000000,
12+
'green': 0x00ff00,
13+
'orange': 0xffa500,
14+
'purple': 0x9147ff,
15+
'red': 0xff0000,
16+
'white': 0xffffff,
17+
'yellow': 0xffff00,
18+
}
19+
20+
1021
def get_bot_avatar(gravatar: str) -> str:
1122
"""
1223
Get Gravatar image url.
@@ -36,21 +47,23 @@ def get_avatar_bytes():
3647
return avatar_img
3748

3849

39-
def get_data_dir():
50+
def get_app_dirs():
4051
# parent directory name of this file, not full path
41-
parent_dir = os.path.dirname(os.path.abspath(__file__)).split(os.sep)[-2]
52+
parent_dir = os.path.dirname(os.path.abspath(__file__)).split(os.sep)[-3]
4253
if parent_dir == 'app': # running in Docker container
54+
a = '/app'
4355
d = '/data'
4456
else: # running locally
57+
a = os.getcwd()
4558
d = os.path.join(os.getcwd(), 'data')
4659
os.makedirs(d, exist_ok=True)
47-
return d
60+
return a, d
4861

4962

5063
# constants
5164
avatar = get_bot_avatar(gravatar=os.environ['GRAVATAR_EMAIL'])
5265
org_name = 'LizardByte'
5366
bot_name = f'{org_name}-Bot'
5467
bot_url = 'https://app.lizardbyte.dev'
55-
data_dir = get_data_dir()
68+
app_dir, data_dir = get_app_dirs()
5669
version = '0.0.0'

0 commit comments

Comments
 (0)