more refinements for the deployment
All checks were successful
Build container / Build (pull_request) Successful in 1m10s
All checks were successful
Build container / Build (pull_request) Successful in 1m10s
This commit is contained in:
29
.drone.yml
29
.drone.yml
@@ -1,29 +0,0 @@
|
||||
kind: pipeline
|
||||
type: kubernetes
|
||||
name: docker-build
|
||||
|
||||
node_selector:
|
||||
kubernetes.io/arch: amd64
|
||||
|
||||
|
||||
steps:
|
||||
- name: docker
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_uname
|
||||
password:
|
||||
from_secret: docker_pw
|
||||
|
||||
repo: mollre/journal-bot
|
||||
tags:
|
||||
- 1.0.${DRONE_BUILD_NUMBER}
|
||||
- latest
|
||||
build_args: "BOT_VERSION=1.0.${DRONE_BUILD_NUMBER}"
|
||||
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- main
|
||||
event:
|
||||
- push
|
@@ -2,6 +2,9 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
name: Build container
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
from pathlib import Path
|
||||
from peewee import *
|
||||
|
||||
db = DatabaseProxy()
|
||||
|
||||
class BaseModel(Model):
|
||||
@@ -13,7 +13,7 @@ class ListModel(BaseModel):
|
||||
@property
|
||||
def content(self) -> dict:
|
||||
return {e.id: e.entry for e in self.entries}
|
||||
|
||||
|
||||
@content.setter
|
||||
def content(self, new_content: dict):
|
||||
old_content = self.content
|
||||
@@ -29,7 +29,7 @@ class ListModel(BaseModel):
|
||||
@property
|
||||
def done_dict(self):
|
||||
return {e.id: e.done for e in self.entries}
|
||||
|
||||
|
||||
@done_dict.setter
|
||||
def done_dict(self, new_done: dict):
|
||||
old_done_dict = self.done_dict
|
||||
@@ -46,7 +46,8 @@ class ListEntryModel(BaseModel):
|
||||
done = BooleanField(default=None, null=True)
|
||||
|
||||
|
||||
def set_db(db_path):
|
||||
def set_db(db_path: Path):
|
||||
db_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
db.initialize(SqliteDatabase(db_path))
|
||||
with db:
|
||||
db.create_tables([ListModel, ListEntryModel], safe=True)
|
||||
|
@@ -7,10 +7,14 @@ from telegram.constants import ParseMode
|
||||
import os
|
||||
|
||||
FIRST = 1
|
||||
import models
|
||||
from .basehandler import BaseHandler
|
||||
|
||||
|
||||
|
||||
class StatusHandler(BaseHandler):
|
||||
"""Shows a short status of the program."""
|
||||
|
||||
|
||||
def __init__(self, entry_string):
|
||||
self.start_time = datetime.datetime.now()
|
||||
self.entry_string = entry_string
|
||||
@@ -35,7 +39,6 @@ class StatusHandler(BaseHandler):
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
|
||||
delta = str(datetime.datetime.now() - self.start_time)
|
||||
message = "BeebBop, this is Norbit\n"
|
||||
|
||||
try:
|
||||
ip = httpx.get('https://api.ipify.org').text
|
||||
@@ -47,18 +50,21 @@ class StatusHandler(BaseHandler):
|
||||
ip = "not fetchable"
|
||||
local_ips = "not fetchable"
|
||||
|
||||
message += "Status: Running 🟢\n"
|
||||
message += f"Version: `{os.getenv('BOT_VERSION', 'dev')}`\n"
|
||||
message += f"Uptime: `{delta[:delta.rfind('.')]}`\n"
|
||||
message += f"IP \(public\): `{ip}`\n"
|
||||
message += f"IP \(private\): `{local_ips}`\n"
|
||||
message += f"Chat ID: `{update.effective_chat.id}`\n"
|
||||
message = f"""
|
||||
BeebBop\!
|
||||
Status: Running 🟢
|
||||
Version: `{os.getenv('BOT_VERSION', 'dev')}` and`prod={models.IS_PRODUCTION}`
|
||||
Uptime: `{delta[:delta.rfind('.')]}`
|
||||
IP \(public\): `{ip}`
|
||||
IP \(private\): `{local_ips}`
|
||||
Chat ID: `{update.effective_chat.id}`
|
||||
""".strip() # remove trailing whitespace
|
||||
|
||||
if update.message:
|
||||
await update.message.reply_text(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN_V2)
|
||||
else:
|
||||
await update._effective_chat.send_message(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN_V2)
|
||||
|
||||
|
||||
return FIRST
|
||||
|
||||
|
||||
|
@@ -14,7 +14,7 @@ class SetChatPhotoJob():
|
||||
self.bot = bot
|
||||
self.logger = logging.getLogger(self.__class__.__name__)
|
||||
|
||||
if models.IS_PRODUCTION:
|
||||
if not models.IS_PRODUCTION:
|
||||
# when running locally, annoy the programmer every 60 seconds <3
|
||||
job_queue.run_repeating(self.callback_photo, interval=60)
|
||||
else:
|
||||
|
95
bot/cronjob/leaderboard.py
Normal file
95
bot/cronjob/leaderboard.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import os
|
||||
from telegram.ext import ExtBot
|
||||
from telegram.constants import ParseMode
|
||||
import logging
|
||||
from datetime import time, timedelta, timezone, datetime, date
|
||||
from peewee import fn
|
||||
import models
|
||||
from telegram.ext import JobQueue
|
||||
|
||||
|
||||
RANKING_TEMPLATE = """
|
||||
<b>Journal Leaderboard</b>
|
||||
This week: 📈{week_leader_name} - {week_leader_count} 📉{week_last_name} - {week_last_count}
|
||||
This month: 📈{month_leader_name} - {month_leader_count} 📉{month_last_name} - {month_last_count}
|
||||
This year: 📈{year_leader_name} - {year_leader_count} 📉{year_last_name} - {year_last_count}
|
||||
|
||||
🏆 Leader: {leader_name}
|
||||
"""
|
||||
|
||||
|
||||
|
||||
def get_author_ranking(since_days):
|
||||
"""Returns the query for the top authors by counting their journal entries. An additional field for the count is added."""
|
||||
|
||||
cutoff_date = date.today() - timedelta(days=since_days)
|
||||
with models.db:
|
||||
return models.JournalEntry.select(
|
||||
models.JournalEntry.author,
|
||||
fn.Count(models.JournalEntry.id).alias('message_count')
|
||||
).where(
|
||||
models.JournalEntry.date >= cutoff_date
|
||||
).group_by(
|
||||
models.JournalEntry.author
|
||||
).order_by(
|
||||
fn.Count(models.JournalEntry.id).desc()
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
class SendLeaderboard():
|
||||
def __init__(self, bot: ExtBot, job_queue: JobQueue):
|
||||
self.bot = bot
|
||||
self.logger = logging.getLogger(self.__class__.__name__)
|
||||
|
||||
if not models.IS_PRODUCTION:
|
||||
# when running locally, just run once after 10 seconds
|
||||
job_queue.run_once(self.callback_leaderboard, when=10)
|
||||
else:
|
||||
# set the message sending time; include UTC shift +2
|
||||
sending_time = time(hour=12, minute=0, second=0, tzinfo=timezone(timedelta(hours=2)))
|
||||
job_queue.run_daily(self.callback_leaderboard, when=sending_time, day=-1)
|
||||
|
||||
|
||||
async def callback_leaderboard(self, context):
|
||||
"""Send a weakly leaderboard to the chat."""
|
||||
if date.today().weekday() != 1:
|
||||
self.logger.info("Today is not Monday, skipping leaderboard.")
|
||||
return
|
||||
|
||||
# get the top contributions of the past week, month and year:
|
||||
ranking_week = get_author_ranking(7)
|
||||
ranking_month = get_author_ranking(30)
|
||||
ranking_year = get_author_ranking(365)
|
||||
|
||||
week_leader, week_last = ranking_week.first(n=2)
|
||||
month_leader, month_last = ranking_month.first(n=2)
|
||||
year_leader, year_last = ranking_year.first(n=2)
|
||||
|
||||
leader = year_leader
|
||||
|
||||
message_text = RANKING_TEMPLATE.format(
|
||||
week_leader_name=week_leader.author,
|
||||
week_leader_count=week_leader.message_count,
|
||||
week_last_name=week_last.author,
|
||||
week_last_count=week_last.message_count,
|
||||
month_leader_name=month_leader.author,
|
||||
month_leader_count=month_leader.message_count,
|
||||
month_last_name=month_last.author,
|
||||
month_last_count=month_last.message_count,
|
||||
year_leader_name=year_leader.author,
|
||||
year_leader_count=year_leader.message_count,
|
||||
year_last_name=year_last.author,
|
||||
year_last_count=year_last.message_count,
|
||||
leader_name=leader.author
|
||||
)
|
||||
|
||||
print(message_text)
|
||||
|
||||
chat_id = os.getenv("CHAT_ID")
|
||||
await self.bot.send_message(
|
||||
chat_id = chat_id,
|
||||
text = message_text,
|
||||
parse_mode=ParseMode.HTML
|
||||
)
|
@@ -11,7 +11,7 @@ class RandomMemoryJob():
|
||||
self.bot = bot
|
||||
self.logger = logging.getLogger(self.__class__.__name__)
|
||||
|
||||
if models.IS_PRODUCTION:
|
||||
if not models.IS_PRODUCTION:
|
||||
# when running locally, annoy the programmer every 60 seconds <3
|
||||
job_queue.run_repeating(self.callback_memory, interval=3600)
|
||||
self.min_age = 0 # do not filter messages: show them all
|
||||
|
@@ -5,7 +5,7 @@ import logging
|
||||
import models
|
||||
from commands import journal, status, turtle, memory, advent
|
||||
from commands.list import list
|
||||
from cronjob import chat_photo, random_memory
|
||||
from cronjob import chat_photo, random_memory, leaderboard
|
||||
|
||||
logging.basicConfig(
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
@@ -32,6 +32,7 @@ def main() -> None:
|
||||
|
||||
random_memory.RandomMemoryJob(application.bot, application.job_queue)
|
||||
chat_photo.SetChatPhotoJob(application.bot, application.job_queue)
|
||||
leaderboard.SendLeaderboard(application.bot, application.job_queue)
|
||||
|
||||
# Run the bot until the user presses Ctrl-C
|
||||
application.run_polling()
|
||||
|
@@ -79,13 +79,13 @@ class JournalEntry(BaseModel):
|
||||
"""Returns the text with all the frisky details hidden away"""
|
||||
new_text = self.text.replace("<", "<").replace(">", ">").replace("&", "&")
|
||||
pattern = re.compile(
|
||||
"("
|
||||
"(((?<=(\.|\!|\?)\s)[A-Z])|(^[A-Z]))" # beginning of a sentence
|
||||
"([^\.\!\?])+" # any character being part of a sentence
|
||||
"((\:\))|😇|😈|[Ss]ex)" # the smiley
|
||||
"([^\.\!\?])*" # continuation of sentence
|
||||
"(\.|\!|\?|\,|$)" # end of the sentence
|
||||
")"
|
||||
r"("
|
||||
r"(((?<=(\.|\!|\?)\s)[A-Z])|(^[A-Z]))" # beginning of a sentence
|
||||
r"([^\.\!\?])+" # any character being part of a sentence
|
||||
r"((\:\))|😇|😈|[Ss]ex)" # the smiley
|
||||
r"([^\.\!\?])*" # continuation of sentence
|
||||
r"(\.|\!|\?|\,|$)" # end of the sentence
|
||||
r")"
|
||||
)
|
||||
matches = pattern.findall(new_text)
|
||||
for match in matches:
|
||||
|
@@ -17,7 +17,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: journal
|
||||
image: mollre/journal-bot:1.0.19
|
||||
image: journal
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: journal-secret-env
|
||||
@@ -33,31 +33,3 @@ spec:
|
||||
- name: journal-nfs
|
||||
persistentVolumeClaim:
|
||||
claimName: journal-data-nfs
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: "journal-data-nfs"
|
||||
spec:
|
||||
storageClassName: ""
|
||||
capacity:
|
||||
storage: "5Gi"
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
nfs:
|
||||
path: /export/kluster/journal-bot
|
||||
server: 192.168.1.157
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: "journal-data-nfs"
|
||||
spec:
|
||||
storageClassName: ""
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: "5Gi"
|
||||
volumeName: journal-data-nfs
|
||||
|
||||
|
@@ -1,9 +1,15 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- ./namespace.yaml
|
||||
- ./deployment.yaml
|
||||
- ./sealedsecret.yaml
|
||||
- ./pvc.yaml
|
||||
|
||||
namespace: journal-bot
|
||||
|
||||
images:
|
||||
- name: mollre/journal-bot
|
||||
newTag: 1.0.68
|
||||
- name: journal
|
||||
newName: git.kluster.moll.re/remoll/journal-bot
|
||||
newTag: 29d951427d6f3377e43767916cefb07e03e9eab8
|
||||
|
@@ -1,6 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: journal
|
||||
labels:
|
||||
name: journal
|
||||
name: placeholder
|
||||
|
27
deployment/pvc.yaml
Normal file
27
deployment/pvc.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: "journal-data-nfs"
|
||||
spec:
|
||||
storageClassName: ""
|
||||
capacity:
|
||||
storage: "5Gi"
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
nfs:
|
||||
path: /export/kluster/journal-bot
|
||||
server: 192.168.1.157
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: "journal-data-nfs"
|
||||
spec:
|
||||
storageClassName: ""
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: "5Gi"
|
||||
volumeName: journal-data-nfs
|
||||
|
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"packageRules": [
|
||||
{
|
||||
"matchUpdateTypes": ["minor", "patch"],
|
||||
"matchCurrentVersion": "!/^0/",
|
||||
"automerge": true,
|
||||
"automergeType": "branch",
|
||||
"ignoreTests": true
|
||||
}
|
||||
],
|
||||
"commitMessagePrefix" : "[CI SKIP]"
|
||||
}
|
14
renovate.json5
Normal file
14
renovate.json5
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"dependencyDashboard": true,
|
||||
"packageRules": [
|
||||
// Fully automatically update the container version referenced in the deployment
|
||||
{
|
||||
"matchPackageNames": ["@kubernetes-sigs/kustomize"],
|
||||
"automerge": true,
|
||||
"automergeType": "branch",
|
||||
"ignoreTests": true
|
||||
}
|
||||
],
|
||||
"commitMessagePrefix" : "[skip ci]"
|
||||
}
|
Reference in New Issue
Block a user