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): | ||||
| @@ -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,7 +7,11 @@ 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.""" | ||||
|  | ||||
| @@ -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,12 +50,15 @@ 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) | ||||
|   | ||||
| @@ -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