mirror of
				https://github.com/osmarks/autobotrobot
				synced 2025-10-31 07:53:00 +00:00 
			
		
		
		
	basic achievement system
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								assets/achievements/spam.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/achievements/spam.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 943 B | 
							
								
								
									
										
											BIN
										
									
								
								assets/achievements/spectre_of_communism.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/achievements/spectre_of_communism.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 901 B | 
							
								
								
									
										
											BIN
										
									
								
								assets/achievements/test.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/achievements/test.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 921 B | 
							
								
								
									
										
											BIN
										
									
								
								assets/achievements/unicode_abuse.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/achievements/unicode_abuse.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 589 B | 
							
								
								
									
										68
									
								
								src/achievement.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/achievement.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | |||||||
|  | from discord.ext import commands | ||||||
|  | import discord | ||||||
|  | import logging | ||||||
|  | import asyncio | ||||||
|  | import discord | ||||||
|  | from datetime import datetime | ||||||
|  | import re | ||||||
|  | import collections | ||||||
|  |  | ||||||
|  | import util | ||||||
|  |  | ||||||
|  | Achievement = collections.namedtuple("Achievement", ["name", "condition", "description"]) | ||||||
|  |  | ||||||
|  | achievements = { | ||||||
|  |     "spectre_of_communism": Achievement("Containment Efforts Ongoing", "Refer to the 'spectre of communism' in a message.", "A spectre is haunting Europe. The spectre of communism. Containment efforts are ongoing and full containment is projected by 2036."), | ||||||
|  |     "test": Achievement("Test", "Test achievement. Obtained for testing.", "Congratulations, you ran the test command!"), | ||||||
|  |     "spam": Achievement("beesbeesbeesbeesbeesbeesbeesbeesbees", "Send a long message containing the same thing repeatedly.", "You should probably not do this, nobody* likes spam!"), | ||||||
|  |     "unicode_abuse": Achievement("Anomalous Unicode", "Send a high proportion of weird Unicode characters in a message.", "h̵͖̻̮̗̹̆͛͆̎ͮͤͫ͛ͦ̓̅ͤ́͢é͒ͧ̌̀ͪ̈͂̈́̉ͣ̅̿̄̌̋̿̽̚͏̛͏͚̯͉̟͇̼̹͎ͅa̠̹̘͎̫̜̞̩͖̟̟͍͇͈͍̝͕͛ͥ͊̾̈́ͩͯͩͭ̆̋͐͗̉͋̓̀͝v͎͖̜͎͔̞͚͉̺̞̘̥͖̝͚̺̍ͤ̌͂ͨ̃̅ͫ̿͛ͯ̓̉̆̎͊̀̚̕͟s̪̠̟̣̝̹̭̻̈́ͤ͗̏ͮ̂ͯ̈́̊ͩ̓̆̌̆͌̽̓̈́̚͢͞e̛̞̙̜̗̰͕͕͎̺͍̭̲̟̭̲̫̬͓ͯ̅̓̆̂̔̃͟r̷̛̮̮͇̳̳̾ͯͮͩ̏͂ͤ̿̽ͧ͒͋́̕ͅͅv̴̠͉̼̮̭̘ͪͯͦ͌́ͯ̒̃̀́̃͜͝ͅe̵̷̢͕̣̻̥̲͓̼͍̱͕̮̯̱̤̹̱̝̎̓̈́̿ͤ̔̍ͭͭ͐ͅŗ̔ͮͯ͂́͏̻͈̱ͅ ̣͇̼͊̄ͫ̆̍̄̀̀̓͊͐͋̌͘͠į̱͔̰̭̫̱̫̊ͪ̅ͥ̈́ͥ̐͌̅ͪ̅ͨ̎̀͘͝s̍͑̌̋̅͌͂ͨͬͯ̇͊҉̛̱̺͕̰͓̗̖̬͡͡ ̥̤̺̖̪̪́ͯͣ̏̅̈ͣ̿̀͠͠͞i̢̛̭̰̻͈̦̣̮̞̤̩̊̌̾͛ͭͦ̆ͮ̃̎ͪ̔ͬ͊̆͂ͫͅn̸̖͚̣̪̩̏ͥ̈́̅ͯ̔͆́ͦ͗͛͒̃̃ͫ͟͜͝͠ȩ̸͎̟̣̞͉̫̗̙̻̯͍̰̣̌ͪͨ͛̆̕͡v̙͙̲͕͔̦̣̺͔̖͉̜̲̩̈̿ͥ̎͊̈́̊ͯͯ͒ͭ̊̀͢i̪͈̣̱̞̥̰̟̣̩̼̻̪̳̤͇̻̹͉͗ͭ͆̆̎̀͑͑̆͋̏̏͊ͣͦ͆ͣ̈́̓͟͢ţ̵̘̫̯͓̻̗͕̘͙̯̞̪̪̲̤̬̜͕ͫ̄̌̓̎͌ͧ̔͟͢ͅa̸̧̭̲̯̳̔́͋̐͂̇ͪ̔̐́̚͢b͐̅̔ͭ͗̊̂̾̀̓ͭͭ͑ͤ̏̐̃ͩͬ҉̞̼̮̤̝̲̳͓̗̤̫̭̝̹̙͘͟͝ļ̷͈̭̖͓̜̬͔̻͔̀̎ͯ͗̐̽̏ͦ̊͗ͧ́͘ͅe̢͍̦̗̬̝̠͔̳̣̯̮̣̹͍͙̞̜ͣ̉͆̊̀̎ͦ͌̂̋̊ͨ͛́") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async def achieve(bot: commands.Bot, message: discord.Message, achievement): | ||||||
|  |     guild_conf = await bot.database.execute_fetchone("SELECT achievement_messages FROM guild_config WHERE id = ?", (message.guild.id,)) | ||||||
|  |     if guild_conf and guild_conf["achievement_messages"] == 0: return | ||||||
|  |  | ||||||
|  |     uid = message.author.id | ||||||
|  |     # ensure the user doesn't have achievements off | ||||||
|  |     conf = await bot.database.execute_fetchone("SELECT * FROM user_config") | ||||||
|  |     if conf and conf["achievement_tracking_enabled"] == 0: return | ||||||
|  |     if not conf: | ||||||
|  |         await bot.database.execute("INSERT INTO user_config VALUES (?, NULL)", (uid,)) | ||||||
|  |         await bot.database.commit() | ||||||
|  |     # detect if achievement already earned | ||||||
|  |     if await bot.database.execute_fetchone("SELECT 1 FROM achievements WHERE user_id = ? AND achievement = ?", (uid, achievement)): | ||||||
|  |         return | ||||||
|  |     achievement_info = achievements[achievement] | ||||||
|  |     description = f"Congratulations! You achieved the achievement __{achievement_info.name}__.\n\n{achievement_info.description}\n*{achievement_info.condition}*" | ||||||
|  |     e = util.make_embed(description=description, title="Achievement achieved!", color=util.hashbow(achievement)) | ||||||
|  |     e.set_thumbnail(url=await util.get_asset(bot, f"achievements/{achievement}.png")) | ||||||
|  |     await message.channel.send(embed=e) | ||||||
|  |     await bot.database.execute("INSERT INTO achievements VALUES (?, ?, ?)", (uid, achievement, util.timestamp())) | ||||||
|  |     await bot.database.commit() | ||||||
|  |     logging.info("awarded achievement %s to %s", message.author.name, achievement) | ||||||
|  |  | ||||||
|  | def setup(bot): | ||||||
|  |     @bot.group(name="achievements", aliases=["ach", "achieve", "achievement"], brief="Achieve a wide variety of fun achievements!", help=f""" | ||||||
|  |     Do things and get arbitrary achievements for them! | ||||||
|  |     Note that due to reasons messages for achievements will not be shown except in opted-in servers, although achievements will be gained regardless. | ||||||
|  |     """) | ||||||
|  |     async def achievements(ctx): pass | ||||||
|  |  | ||||||
|  |     @achievements.command(help="Enable/disable achievement messages on this guild.") | ||||||
|  |     @commands.check(util.server_mod_check(bot)) | ||||||
|  |     async def set_enabled(ctx, on: bool): | ||||||
|  |         await bot.database.execute("INSERT OR REPLACE INTO guild_config VALUES (?, ?)", (ctx.guild.id, int(on))) | ||||||
|  |         await bot.database.commit() | ||||||
|  |         await ctx.send(f"Achievement messages set to: {on}") | ||||||
|  |  | ||||||
|  |     @achievements.command(help="Obtain a test achievement") | ||||||
|  |     async def test(ctx): | ||||||
|  |         await achieve(ctx.bot, ctx.message, "test") | ||||||
|  |  | ||||||
|  |     @bot.listen("on_message") | ||||||
|  |     async def message_listener(msg: discord.Message): | ||||||
|  |         content = msg.content | ||||||
|  |         content_len = len(msg.content) | ||||||
|  |         if re.match("spect(re|er).{,20}(communism|☭)", content): await achieve(bot, msg, "spectre_of_communism") | ||||||
|  |         if re.match(r"^(.+)\1+$", content) and len(content) >= 1950: await achieve(bot, msg, "spam") | ||||||
|  |         if content_len > 30 and (len(re.findall("[\u0300-\u036f\U00040000-\U0010FFFF]", content)) / content_len) > 0.35: await achieve(bot, msg, "unicode_abuse") | ||||||
							
								
								
									
										31
									
								
								src/db.py
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/db.py
									
									
									
									
									
								
							| @@ -36,6 +36,37 @@ CREATE TABLE calls ( | |||||||
|     to_id TEXT NOT NULL REFERENCES telephone_config(id), |     to_id TEXT NOT NULL REFERENCES telephone_config(id), | ||||||
|     start_time INTEGER NOT NULL |     start_time INTEGER NOT NULL | ||||||
| ); | ); | ||||||
|  | """, | ||||||
|  | """ | ||||||
|  | CREATE TABLE guild_config ( | ||||||
|  |     id INTEGER PRIMARY KEY, | ||||||
|  |     achievement_messages INTEGER | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | CREATE TABLE user_config ( | ||||||
|  |     id INTEGER PRIMARY KEY, | ||||||
|  |     achievement_tracking_enabled INTEGER | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | CREATE TABLE stats ( | ||||||
|  |     user_id INTEGER NOT NULL REFERENCES user_config (id), | ||||||
|  |     stat TEXT NOT NULL COLLATE NOCASE, | ||||||
|  |     value BLOB NOT NULL, | ||||||
|  |     UNIQUE (user_id, stat) | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | CREATE TABLE achievements ( | ||||||
|  |     user_id INTEGER NOT NULL REFERENCES user_config (id), | ||||||
|  |     achievement TEXT NOT NULL, | ||||||
|  |     achieved_time INTEGER NOT NULL, | ||||||
|  |     UNIQUE (user_id, achievement) | ||||||
|  | ); | ||||||
|  | """, | ||||||
|  | """ | ||||||
|  | CREATE TABLE assets ( | ||||||
|  |     identifier TEXT PRIMARY KEY, | ||||||
|  |     url TEXT NOT NULL | ||||||
|  | ); | ||||||
| """ | """ | ||||||
| ] | ] | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import argparse | |||||||
| import traceback | import traceback | ||||||
| import random | import random | ||||||
| import rolldice | import rolldice | ||||||
|  | #import aiopubsub | ||||||
|  |  | ||||||
| import tio | import tio | ||||||
| import db | import db | ||||||
| @@ -204,14 +205,16 @@ async def andrew_bad(ctx): | |||||||
| @bot.event | @bot.event | ||||||
| async def on_ready(): | async def on_ready(): | ||||||
|     logging.info("Connected as " + bot.user.name) |     logging.info("Connected as " + bot.user.name) | ||||||
|     await bot.change_presence(status=discord.Status.online, activity=discord.Activity(type=discord.ActivityType.listening, name=f"commands beginning with {bot.command_prefix}")) |     await bot.change_presence(status=discord.Status.online,  | ||||||
|  |         activity=discord.CustomActivity(name=f"{bot.command_prefix}help")) | ||||||
|  |  | ||||||
| async def run_bot(): | async def run_bot(): | ||||||
|     bot.database = await db.init(config["database"]) |     bot.database = await db.init(config["database"]) | ||||||
|     for ext in ( |     for ext in ( | ||||||
|         "reminders", |         "reminders", | ||||||
|         "debug", |         "debug", | ||||||
|         "telephone" |         "telephone", | ||||||
|  |         "achievement" | ||||||
|     ): |     ): | ||||||
|         bot.load_extension(ext) |         bot.load_extension(ext) | ||||||
|     await bot.start(config["token"]) |     await bot.start(config["token"]) | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								src/util.py
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/util.py
									
									
									
									
									
								
							| @@ -8,6 +8,8 @@ from dateutil.relativedelta import relativedelta | |||||||
| import json | import json | ||||||
| import discord | import discord | ||||||
| import toml | import toml | ||||||
|  | import os.path | ||||||
|  | from discord.ext import commands | ||||||
|  |  | ||||||
| config = toml.load(open("config.toml", "r")) | config = toml.load(open("config.toml", "r")) | ||||||
|  |  | ||||||
| @@ -229,4 +231,18 @@ def json_encode(x): return json.dumps(x, separators=(',', ':')) | |||||||
| def server_mod_check(bot): | def server_mod_check(bot): | ||||||
|     async def check(ctx): |     async def check(ctx): | ||||||
|         return ctx.author.permissions_in(ctx.channel).manage_channels or (await bot.is_owner(ctx.author)) |         return ctx.author.permissions_in(ctx.channel).manage_channels or (await bot.is_owner(ctx.author)) | ||||||
|     return check |     return check | ||||||
|  |  | ||||||
|  | async def get_asset(bot: commands.Bot, identifier): | ||||||
|  |     safe_ident = re.sub("[^A-Za-z0-9_.-]", "_", identifier) | ||||||
|  |     x = await bot.database.execute_fetchone("SELECT * FROM assets WHERE identifier = ?", (safe_ident,)) | ||||||
|  |     if x: | ||||||
|  |         return x["url"] | ||||||
|  |     file = discord.File(os.path.join("./assets", identifier), filename=safe_ident) | ||||||
|  |     message = await (bot.get_channel(config["image_upload_channel"])).send(identifier, file=file) | ||||||
|  |     url = message.attachments[0].proxy_url | ||||||
|  |     await bot.database.execute("INSERT INTO assets VALUES (?, ?)", (safe_ident, url)) | ||||||
|  |     return url | ||||||
|  |  | ||||||
|  | def hashbow(thing): | ||||||
|  |     return hash(thing) % 0x1000000 | ||||||
		Reference in New Issue
	
	Block a user