Updated to discord bot and its docker
gitignore dockerignore Formatting Requirement update, code styling Gitignore
This commit is contained in:
parent
00c3959273
commit
71e69719d7
13
.dockerignore
Normal file
13
.dockerignore
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
__pycache__
|
||||||
|
# Docker
|
||||||
|
docker-compose.yml
|
||||||
|
.docker
|
||||||
|
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*/__pycache__/
|
||||||
|
**/__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
**/*.py[cod]
|
||||||
|
|
||||||
|
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,5 +1,9 @@
|
|||||||
venv
|
venv
|
||||||
*.db
|
*.db
|
||||||
|
*.log
|
||||||
.env
|
.env
|
||||||
|
pid
|
||||||
|
*pid
|
||||||
__pycache__
|
__pycache__
|
||||||
.idea
|
.idea
|
||||||
|
debug/
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
FROM python:3.9-slim
|
FROM python:3.9-slim
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
RUN groupadd -g 1000 discordbot \
|
||||||
|
&& useradd -u 1000 -g 1000 discordbot \
|
||||||
|
&& mkdir /home/discordbot \
|
||||||
|
&& chown -R discordbot:discordbot /app \
|
||||||
|
&& chown -R discordbot:discordbot /home/discordbot
|
||||||
|
|
||||||
|
USER discordbot
|
||||||
COPY requirements.txt /app/requirements.txt
|
COPY requirements.txt /app/requirements.txt
|
||||||
RUN pip install -r requirements.txt
|
RUN pip install -r requirements.txt
|
||||||
COPY . /app
|
|
||||||
|
|
||||||
CMD python discord_bot.py
|
CMD python discord_bot.py
|
||||||
|
4
db.py
4
db.py
@ -1,4 +1,4 @@
|
|||||||
from typing import Union, Dict, Optional
|
from typing import Dict, Optional, Union
|
||||||
|
|
||||||
from sqlite_utils import Database
|
from sqlite_utils import Database
|
||||||
from sqlite_utils.db import NotFoundError
|
from sqlite_utils.db import NotFoundError
|
||||||
@ -21,7 +21,7 @@ class DiscordDB:
|
|||||||
self._db.create_table("player", {"id": int, "name": str}, pk="id", not_null={"id", "name"})
|
self._db.create_table("player", {"id": int, "name": str}, pk="id", not_null={"id", "name"})
|
||||||
|
|
||||||
if "epic" not in self._db.table_names():
|
if "epic" not in self._db.table_names():
|
||||||
self._db.create_table("epic", {"id": int, }, pk="id", not_null={"id"})
|
self._db.create_table("epic", {"id": int, "fake": bool}, pk="id", not_null={"id"}, defaults={"fake": False})
|
||||||
|
|
||||||
self._db.vacuum()
|
self._db.vacuum()
|
||||||
|
|
||||||
|
137
discord_bot.py
137
discord_bot.py
@ -18,8 +18,7 @@ APP_NAME = "discord_bot"
|
|||||||
|
|
||||||
os.chdir(os.path.abspath(os.path.dirname(sys.argv[0])))
|
os.chdir(os.path.abspath(os.path.dirname(sys.argv[0])))
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
logging.basicConfig(level=logging.WARNING, filename="logging.log",
|
logging.basicConfig(level=logging.WARNING, filename="logging.log", format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
||||||
logger = logging.getLogger(APP_NAME)
|
logger = logging.getLogger(APP_NAME)
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
logger.propagate = False
|
logger.propagate = False
|
||||||
@ -28,29 +27,94 @@ fh.setLevel(logging.DEBUG)
|
|||||||
logger.addHandler(fh)
|
logger.addHandler(fh)
|
||||||
keep_fds = [fh.stream.fileno()]
|
keep_fds = [fh.stream.fileno()]
|
||||||
|
|
||||||
os.makedirs('debug', exist_ok=True)
|
os.makedirs("debug", exist_ok=True)
|
||||||
|
|
||||||
pidfile = "pid"
|
pidfile = "pid"
|
||||||
with open(pidfile, 'w') as f:
|
with open(pidfile, "w") as f:
|
||||||
f.write(str(os.getpid()))
|
f.write(str(os.getpid()))
|
||||||
|
|
||||||
DISCORD_TOKEN = os.getenv("DISCORD_TOKEN")
|
DISCORD_TOKEN = os.getenv("DISCORD_TOKEN")
|
||||||
DEFAULT_CHANNEL_ID = os.getenv("DEFAULT_CHANNEL_ID", 603527159109124096)
|
DEFAULT_CHANNEL_ID = os.getenv("DEFAULT_CHANNEL_ID", 603527159109124096)
|
||||||
ADMIN_ID = os.getenv("DEFAULT_CHANNEL_ID", 220849530730577920)
|
ADMIN_ID = os.getenv("DEFAULT_CHANNEL_ID", 220849530730577920)
|
||||||
DB_NAME = os.getenv('DB_NAME', 'discord.db')
|
DB_NAME = os.getenv("DB_NAME", "discord.db")
|
||||||
DB = DiscordDB(DB_NAME)
|
DB = DiscordDB(DB_NAME)
|
||||||
|
|
||||||
FLAGS = {1: 'flag_ro', 9: 'flag_br', 10: 'flag_it', 11: 'flag_fr', 12: 'flag_de', 13: 'flag_hu', 14: 'flag_cn',
|
FLAGS = {
|
||||||
15: 'flag_es', 23: 'flag_ca', 24: 'flag_us', 26: 'flag_mx', 27: 'flag_ar', 28: 'flag_ve', 29: 'flag_gb',
|
1: "flag_ro",
|
||||||
30: 'flag_ch', 31: 'flag_nl', 32: 'flag_be', 33: 'flag_at', 34: 'flag_cz', 35: 'flag_pl', 36: 'flag_sk',
|
9: "flag_br",
|
||||||
37: 'flag_no', 38: 'flag_se', 39: 'flag_fi', 40: 'flag_ua', 41: 'flag_ru', 42: 'flag_bg', 43: 'flag_tr',
|
10: "flag_it",
|
||||||
44: 'flag_gr', 45: 'flag_jp', 47: 'flag_kr', 48: 'flag_in', 49: 'flag_id', 50: 'flag_au', 51: 'flag_za',
|
11: "flag_fr",
|
||||||
52: 'flag_md', 53: 'flag_pt', 54: 'flag_ie', 55: 'flag_de', 56: 'flag_ir', 57: 'flag_pk', 58: 'flag_il',
|
12: "flag_de",
|
||||||
59: 'flag_th', 61: 'flag_si', 63: 'flag_hr', 64: 'flag_cl', 65: 'flag_rs', 66: 'flag_my', 67: 'flag_ph',
|
13: "flag_hu",
|
||||||
68: 'flag_sg', 69: 'flag_ba', 70: 'flag_ee', 71: 'flag_lv', 72: 'flag_lt', 73: 'flag_kp', 74: 'flag_uy',
|
14: "flag_cn",
|
||||||
75: 'flag_py', 76: 'flag_bo', 77: 'flag_pe', 78: 'flag_co', 79: 'flag_mk', 80: 'flag_me', 81: 'flag_tw',
|
15: "flag_es",
|
||||||
82: 'flag_cy', 83: 'flag_by', 84: 'flag_nz', 164: 'flag_sa', 165: 'flag_eg', 166: 'flag_ae', 167: 'flag_al',
|
23: "flag_ca",
|
||||||
168: 'flag_ge', 169: 'flag_am', 170: 'flag_ng', 171: 'flag_cu'}
|
24: "flag_us",
|
||||||
|
26: "flag_mx",
|
||||||
|
27: "flag_ar",
|
||||||
|
28: "flag_ve",
|
||||||
|
29: "flag_gb",
|
||||||
|
30: "flag_ch",
|
||||||
|
31: "flag_nl",
|
||||||
|
32: "flag_be",
|
||||||
|
33: "flag_at",
|
||||||
|
34: "flag_cz",
|
||||||
|
35: "flag_pl",
|
||||||
|
36: "flag_sk",
|
||||||
|
37: "flag_no",
|
||||||
|
38: "flag_se",
|
||||||
|
39: "flag_fi",
|
||||||
|
40: "flag_ua",
|
||||||
|
41: "flag_ru",
|
||||||
|
42: "flag_bg",
|
||||||
|
43: "flag_tr",
|
||||||
|
44: "flag_gr",
|
||||||
|
45: "flag_jp",
|
||||||
|
47: "flag_kr",
|
||||||
|
48: "flag_in",
|
||||||
|
49: "flag_id",
|
||||||
|
50: "flag_au",
|
||||||
|
51: "flag_za",
|
||||||
|
52: "flag_md",
|
||||||
|
53: "flag_pt",
|
||||||
|
54: "flag_ie",
|
||||||
|
55: "flag_de",
|
||||||
|
56: "flag_ir",
|
||||||
|
57: "flag_pk",
|
||||||
|
58: "flag_il",
|
||||||
|
59: "flag_th",
|
||||||
|
61: "flag_si",
|
||||||
|
63: "flag_hr",
|
||||||
|
64: "flag_cl",
|
||||||
|
65: "flag_rs",
|
||||||
|
66: "flag_my",
|
||||||
|
67: "flag_ph",
|
||||||
|
68: "flag_sg",
|
||||||
|
69: "flag_ba",
|
||||||
|
70: "flag_ee",
|
||||||
|
71: "flag_lv",
|
||||||
|
72: "flag_lt",
|
||||||
|
73: "flag_kp",
|
||||||
|
74: "flag_uy",
|
||||||
|
75: "flag_py",
|
||||||
|
76: "flag_bo",
|
||||||
|
77: "flag_pe",
|
||||||
|
78: "flag_co",
|
||||||
|
79: "flag_mk",
|
||||||
|
80: "flag_me",
|
||||||
|
81: "flag_tw",
|
||||||
|
82: "flag_cy",
|
||||||
|
83: "flag_by",
|
||||||
|
84: "flag_nz",
|
||||||
|
164: "flag_sa",
|
||||||
|
165: "flag_eg",
|
||||||
|
166: "flag_ae",
|
||||||
|
167: "flag_al",
|
||||||
|
168: "flag_ge",
|
||||||
|
169: "flag_am",
|
||||||
|
170: "flag_ng",
|
||||||
|
171: "flag_cu",
|
||||||
|
}
|
||||||
|
|
||||||
MENTION_MAPPING = {1: "D1", 2: "D2", 3: "D3", 4: "D4", 11: "Air"}
|
MENTION_MAPPING = {1: "D1", 2: "D2", 3: "D3", 4: "D4", 11: "Air"}
|
||||||
|
|
||||||
@ -67,20 +131,20 @@ def s_to_human(seconds: Union[int, float]) -> str:
|
|||||||
h = seconds // 3600
|
h = seconds // 3600
|
||||||
m = (seconds - (h * 3600)) // 60
|
m = (seconds - (h * 3600)) // 60
|
||||||
s = seconds % 60
|
s = seconds % 60
|
||||||
return f'{h:01d}:{m:02d}:{s:02d}'
|
return f"{h:01d}:{m:02d}:{s:02d}"
|
||||||
|
|
||||||
|
|
||||||
def get_battle_page():
|
def get_battle_page():
|
||||||
global __last_battle_update_timestamp, __last_battle_response
|
global __last_battle_update_timestamp, __last_battle_response
|
||||||
if int(datetime.datetime.now().timestamp()) >= __last_battle_update_timestamp + 60:
|
if int(datetime.datetime.now().timestamp()) >= __last_battle_update_timestamp + 60:
|
||||||
dt = datetime.datetime.now()
|
dt = datetime.datetime.now()
|
||||||
r = requests.get('https://erep.lv/battles.json')
|
r = requests.get("https://www.erepublik.com/en/military/campaignsJson/list")
|
||||||
try:
|
try:
|
||||||
__last_battle_response = r.json()
|
__last_battle_response = r.json()
|
||||||
except JSONDecodeError:
|
except JSONDecodeError:
|
||||||
logger.warning("Received non json response from erep.lv/battles.json!")
|
logger.warning("Received non json response from erep.lv/battles.json!")
|
||||||
return get_battle_page()
|
return get_battle_page()
|
||||||
__last_battle_update_timestamp = __last_battle_response.get('last_updated', int(dt.timestamp()))
|
__last_battle_update_timestamp = __last_battle_response.get("last_updated", int(dt.timestamp()))
|
||||||
return __last_battle_response
|
return __last_battle_response
|
||||||
|
|
||||||
|
|
||||||
@ -95,11 +159,11 @@ class MyClient(discord.Client):
|
|||||||
return int(datetime.datetime.now().timestamp())
|
return int(datetime.datetime.now().timestamp())
|
||||||
|
|
||||||
async def on_ready(self):
|
async def on_ready(self):
|
||||||
print('Client running')
|
print("Client running")
|
||||||
print('------')
|
print("------")
|
||||||
|
|
||||||
async def on_error(self, event_method, *args, **kwargs):
|
async def on_error(self, event_method, *args, **kwargs):
|
||||||
logger.warning(f'Ignoring exception in {event_method}')
|
logger.warning(f"Ignoring exception in {event_method}")
|
||||||
|
|
||||||
async def report_epics(self):
|
async def report_epics(self):
|
||||||
await self.wait_until_ready()
|
await self.wait_until_ready()
|
||||||
@ -108,28 +172,27 @@ class MyClient(discord.Client):
|
|||||||
while not self.is_closed():
|
while not self.is_closed():
|
||||||
try:
|
try:
|
||||||
r = get_battle_page()
|
r = get_battle_page()
|
||||||
if not isinstance(r.get('battles'), dict):
|
if not isinstance(r.get("battles"), dict):
|
||||||
sleep_seconds = r.get('last_updated') + 60 - self.timestamp
|
sleep_seconds = r.get("last_updated") + 60 - self.timestamp
|
||||||
await asyncio.sleep(sleep_seconds if sleep_seconds > 0 else 0)
|
await asyncio.sleep(sleep_seconds if sleep_seconds > 0 else 0)
|
||||||
continue
|
continue
|
||||||
for bid, battle in r.get('battles', {}).items():
|
for bid, battle in r.get("battles", {}).items():
|
||||||
for div in battle.get('div', {}).values():
|
for div in battle.get("div", {}).values():
|
||||||
if div.get('epic') and not DB.get_epic(div.get('id')):
|
if div.get("epic") > 1 and not DB.get_epic(div.get("id")):
|
||||||
with open(f'debug/{self.timestamp}.json', 'wb') as f:
|
with open(f"debug/{self.timestamp}.json", "w") as f:
|
||||||
json.dump(r, f)
|
json.dump(r, f)
|
||||||
await self.get_channel(DEFAULT_CHANNEL_ID).send(
|
await self.get_channel(DEFAULT_CHANNEL_ID).send(
|
||||||
f"{role_mapping[MENTION_MAPPING[div['div']]]} Epic battle! Round time {s_to_human(self.timestamp - battle['start'])}\n"
|
f"{role_mapping[MENTION_MAPPING[div['div']]]} Epic battle! Round time {s_to_human(self.timestamp - battle['start'])}\n"
|
||||||
f"https://www.erepublik.com/en/military/battlefield/{battle['id']}")
|
f"https://www.erepublik.com/en/military/battlefield/{battle['id']}"
|
||||||
DB.add_epic(div.get('id'))
|
)
|
||||||
|
DB.add_epic(div.get("id"))
|
||||||
|
|
||||||
sleep_seconds = r.get('last_updated') + 60 - self.timestamp
|
sleep_seconds = r.get("last_updated") + 60 - self.timestamp
|
||||||
await asyncio.sleep(sleep_seconds if sleep_seconds > 0 else 0)
|
await asyncio.sleep(sleep_seconds if sleep_seconds > 0 else 0)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await self.get_channel(DEFAULT_CHANNEL_ID).send(
|
|
||||||
f"<@{ADMIN_ID}> Something bad has happened with epic notifier!")
|
|
||||||
logger.error("Discord bot's eRepublik epic watcher died!", exc_info=e)
|
logger.error("Discord bot's eRepublik epic watcher died!", exc_info=e)
|
||||||
try:
|
try:
|
||||||
with open(f"{self.timestamp}.json", 'w') as f:
|
with open(f"debug/{self.timestamp}.json", "w") as f:
|
||||||
f.write(r.text)
|
f.write(r.text)
|
||||||
except NameError:
|
except NameError:
|
||||||
logger.error("There was no Response object!", exc_info=e)
|
logger.error("There was no Response object!", exc_info=e)
|
||||||
@ -139,15 +202,15 @@ class MyClient(discord.Client):
|
|||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
client = MyClient()
|
client = MyClient()
|
||||||
bot = commands.Bot(command_prefix='!')
|
bot = commands.Bot(command_prefix="!")
|
||||||
|
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
print('Bot loaded')
|
print("Bot loaded")
|
||||||
# print(bot.user.name)
|
# print(bot.user.name)
|
||||||
# print(bot.user.id)
|
# print(bot.user.id)
|
||||||
print('------')
|
print("------")
|
||||||
|
|
||||||
|
|
||||||
@bot.command()
|
@bot.command()
|
||||||
|
6
docker_run.sh
Executable file
6
docker_run.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
docker rm -f discord_bot
|
||||||
|
set -e
|
||||||
|
docker build --tag discord_epicbot .
|
||||||
|
docker run --detach -v $PWD:/app --restart=always --name discord_bot discord_epicbot
|
||||||
|
|
4
pyproject.toml
Normal file
4
pyproject.toml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[tool.black]
|
||||||
|
line-length = 140
|
||||||
|
target-version = ['py38', 'py39']
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
discord.py==1.7.3
|
discord.py==1.7.3
|
||||||
requests==2.26.0
|
requests==2.26.0
|
||||||
python-dotenv==0.18.0
|
python-dotenv==0.19.0
|
||||||
sqlite_utils==3.12
|
sqlite_utils==3.13
|
||||||
|
1
run.sh
1
run.sh
@ -1,5 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
python -m venv venv
|
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
echo "Checking queries..."
|
echo "Checking queries..."
|
||||||
python -m unittest
|
python -m unittest
|
||||||
|
18
tests.py
18
tests.py
@ -10,28 +10,28 @@ class TestDatabase(unittest.TestCase):
|
|||||||
self.db = DiscordDB()
|
self.db = DiscordDB()
|
||||||
|
|
||||||
def test_member(self):
|
def test_member(self):
|
||||||
member = {'id': 1200, 'name': 'username'}
|
member = {"id": 1200, "name": "username"}
|
||||||
self.db.add_member(**member)
|
self.db.add_member(**member)
|
||||||
self.assertEqual(self.db.add_member(**member), member)
|
self.assertEqual(self.db.add_member(**member), member)
|
||||||
|
|
||||||
self.assertRaises(NotFoundError, self.db.get_member, member_id=100)
|
self.assertRaises(NotFoundError, self.db.get_member, member_id=100)
|
||||||
self.assertEqual(self.db.get_member(member_id=member['id']), member)
|
self.assertEqual(self.db.get_member(member_id=member["id"]), member)
|
||||||
|
|
||||||
member.update(name="Success")
|
member.update(name="Success")
|
||||||
self.assertTrue(self.db.update_member(member['id'], member['name']))
|
self.assertTrue(self.db.update_member(member["id"], member["name"]))
|
||||||
self.assertEqual(self.db.get_member(member_id=member['id']), member)
|
self.assertEqual(self.db.get_member(member_id=member["id"]), member)
|
||||||
|
|
||||||
def test_player(self):
|
def test_player(self):
|
||||||
player = {'id': 1, 'name': 'plato'}
|
player = {"id": 1, "name": "plato"}
|
||||||
self.assertTrue(self.db.add_player(player['id'], player['name']))
|
self.assertTrue(self.db.add_player(player["id"], player["name"]))
|
||||||
self.assertFalse(self.db.add_player(player['id'], player['name']))
|
self.assertFalse(self.db.add_player(player["id"], player["name"]))
|
||||||
self.assertEqual(self.db.get_player(0), None)
|
self.assertEqual(self.db.get_player(0), None)
|
||||||
self.assertEqual(self.db.get_player(player['id']), player)
|
self.assertEqual(self.db.get_player(player["id"]), player)
|
||||||
|
|
||||||
self.assertFalse(self.db.update_player(0, "Error"))
|
self.assertFalse(self.db.update_player(0, "Error"))
|
||||||
player["name"] = "New name"
|
player["name"] = "New name"
|
||||||
self.assertTrue(self.db.update_player(player["id"], player["name"]))
|
self.assertTrue(self.db.update_player(player["id"], player["name"]))
|
||||||
self.assertEqual(self.db.get_player(player['id']), player)
|
self.assertEqual(self.db.get_player(player["id"]), player)
|
||||||
|
|
||||||
def test_epic(self):
|
def test_epic(self):
|
||||||
self.assertFalse(self.db.get_epic(123456))
|
self.assertFalse(self.db.get_epic(123456))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user