commit e5ab9245d641d25e9b741a29802fa62e0104aa39 Author: Raatty Date: Sun Aug 26 11:00:22 2018 +1200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85f0636 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.vscode +__pycache__ +.vscode +.env +venv \ No newline at end of file diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..29cff6d --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: python bot.py diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..0a8091a --- /dev/null +++ b/bot.py @@ -0,0 +1,132 @@ +import logging +import discord +from discord.ext import commands +import datetime +import psycopg2 +import os +import requests + +logging.basicConfig(level=logging.INFO, style='{', datefmt="%d.%m.%Y %H:%M:%S", + format="\n{asctime} [{levelname:<8}] {name}:\n{message}") + + +def get_pre(bot, msg): + if msg.server.id in bot.prefixes.keys(): + return bot.prefixes[msg.server.id] + else: + return '#!' + + +bot = commands.Bot(description="drpg warden", command_prefix=get_pre) + +if 'DATABASE_URL' in os.environ: + bot.db_conn = psycopg2.connect(os.getenv('DATABASE_URL')) +else: + import heroku3 + heroku_conn = heroku3.from_key(os.getenv('HEROKUAPI')) + db_url = heroku_conn.app('drpg-warden').config()['DATABASE_URL'] + bot.db_conn = psycopg2.connect(db_url) + +bot.prefixes = {} + +with bot.db_conn: + with bot.db_conn.cursor() as cur: + cur.execute("SELECT * FROM prefixes;") + pref = cur.fetchall() + for i in pref: + bot.prefixes[i[0]] = i[1] + +bot.remove_command('help') + + +@bot.command(pass_context=True) +async def help(ctx): + await bot.say("__**DRPG Warden: Help**__\n" + "I am a simple bot with only one real function and that is " + "to remind you about the various cooldowns of the bot " + "'DiscordRPG' and I am mostly fuctionless without it.\n" + "For the common DiscordRPG commands I will begin counting " + "and once the cooldown for that command has expired I will " + "mention you to remind you that you can do the command " + "again.\n" + "Also I will scan all the adventure messages from " + "DiscordRPG and if your HP is below 20% I'll remind you to " + "heal. Stats messages will also be scanned currently you " + "will only get info about your weapon.\n\n" + "**Currently implemented cooldowns:**\n" + " __14 seconds:__ {0}adv\n" + " __300 seconds:__ {0}mine, {0}chop, {0}fish, {0}forage\n" + " __600 seconds:__ {0}search\n" + " __various:__ {0}travel (you must of done {0}location " + "in the same channel within the last 30s, you will get " + "pinged when you arive)\n\n" + "My commands:\n" + " **{0}help** shows this message\n" + " **{0}links** shows a message with links to add me to " + "your server or join my server.\n" + " **{0}setprefix** allows you to change my prefix to " + "what ever you want, if you forget what you set as the " + "prefix and message that mentions me and contains the word " + "prefix I will reply with your current prefix\n\n" + "If you have any questions or sugestion contact my master " + "**raatty#3522**" + .format(ctx.prefix)) + + +@bot.command() +async def links(): + await bot.say('```Link to join my server:```\n' + '\n' + '```Link to add me to your own server:```\n' + '') + + +@bot.command(pass_context=True) +async def announce(ctx, *, something): + if ctx.message.author.id == '140652945032216576': + for s in bot.servers: + for c in s.channels: + if c.permissions_for(s.me).send_messages \ + and str(c.type) == 'text': + await bot.send_message(c, something) + break + + +@bot.command(pass_context=True) +async def setprefix(ctx, prefix): + if len(prefix) == 1 or len(prefix) == 2 and isinstance(prefix, str): + if ctx.message.server.id in bot.prefixes: + with bot.db_conn: + with bot.db_conn.cursor() as cur: + cur.execute("UPDATE prefixes SET prefix='{}' WHERE " + "serverid='{}';".format(prefix, + ctx.message.server.id)) + bot.prefixes[ctx.message.server.id] = prefix + await bot.say('Prefix updated') + else: + with bot.db_conn: + with bot.db_conn.cursor() as cur: + cur.execute("INSERT INTO prefixes (serverid, prefix) " + "VALUES (%s, %s)", (ctx.message.server.id, + prefix)) + bot.prefixes[ctx.message.server.id] = prefix + await bot.say('Prefix updated') + else: + await bot.say('Prefix must be between 1 and 2 charactors long') + + +@bot.event +async def on_ready(): + bot.load_extension("drpg") + await bot.change_presence(game=discord.Game(name='#!adv', type=2)) + await bot.send_message(discord.Object(id="408902143119196161"), + "**Im ready!** " + str(datetime.datetime.now())) + requests.post('https://bots.discord.pw/api/bots/410356358836256768/stats', + headers={"Authorization": os.getenv('BOTS_PW_AUTH')}, + json={"server_count": len(bot.servers)}) + await bot.send_message(discord.Object(id="408902143119196161"), + f'**Connected to {len(bot.servers)} servers**') + + +bot.run(os.getenv('TOKEN')) diff --git a/drpg.py b/drpg.py new file mode 100644 index 0000000..91d5879 --- /dev/null +++ b/drpg.py @@ -0,0 +1,93 @@ +import discord +from discord.ext import commands +import asyncio +from utils import read_adv, read_stats, read_travel, DEST_RE + + +class drpg: + def __init__(self, bot): + self.bot = bot + + @commands.command(pass_context=True) + async def adv(self, ctx): + await asyncio.sleep(14) + await self.bot.say(f'πŸ—ΊοΈ {ctx.message.author.mention} you can ' + f'**{ctx.prefix}adv** again') + + @commands.command(pass_context=True) + async def mine(self, ctx): + await asyncio.sleep(300) + await self.bot.say(f'⛏️ {ctx.message.author.mention} you can ' + f'**{ctx.prefix}mine** again') + + @commands.command(pass_context=True) + async def chop(self, ctx): + await asyncio.sleep(300) + await self.bot.say(f'🌲 {ctx.message.author.mention} you can ' + f'**{ctx.prefix}chop** again') + + @commands.command(pass_context=True) + async def fish(self, ctx): + await asyncio.sleep(300) + await self.bot.say(f'🎣 {ctx.message.author.mention} you can ' + f'**{ctx.prefix}fish** again') + + @commands.command(pass_context=True) + async def forage(self, ctx): + await asyncio.sleep(300) + await self.bot.say(f'πŸ‹ {ctx.message.author.mention} you can ' + f'**{ctx.prefix}forage** again') + + @commands.command(pass_context=True) + async def search(self, ctx): + await asyncio.sleep(600) + await self.bot.say(f'πŸ” {ctx.message.author.mention} you can ' + f'**{ctx.prefix}search** again') + + async def on_message(self, message: discord.Message): + if message.author.id == '170915625722576896': + if message.content.startswith('```diff\n!========['): + try: + usr, hp = read_adv(message.content) + if hp <= 50: + usr = message.server.get_member_named(usr) + msg = f"❀️ **Heal up!** {usr.mention} you only have "\ + f"{hp:.2f}% HP left." + await self.bot.send_message(message.channel, msg) + except Exception as e: + raise + elif message.content.startswith('```diff\n!======== ['): + try: + out = read_stats(message.content) + if out is not None: + await self.bot.send_message(message.channel, out) + except Exception: + pass + elif message.embeds != []: + try: + user, destinations = read_travel(**message.embeds[0]) + print(user) + def travel_check(message): + return message.author.id == '170915625722576896' and \ + message.content.startswith(user) + travel = await self.bot.wait_for_message(timeout=30, + check=travel_check, + channel=message.channel) + user = message.server.get_member_named(user) + if travel is not None: + destination = DEST_RE.search(travel.content) \ + .groups(0)[0] + await asyncio.sleep(destinations[destination]) + m = f'{user.mention} you have arived at {destination}' + await self.bot.send_message(message.channel, m) + except Exception: + raise + elif message.server.me in message.mentions and \ + 'prefix' in message.content.lower(): + m1 = '**The prefix for this server is currently set to:** {}' + m2 = m1.format(str(self.bot.command_prefix(self.bot, message))) + await self.bot.send_message(message.channel, m2) + + +def setup(bot): + bot.add_cog(drpg(bot)) diff --git a/items/weapons.json b/items/weapons.json new file mode 100644 index 0000000..25f83a4 --- /dev/null +++ b/items/weapons.json @@ -0,0 +1,373 @@ +[ + { + "item": "Small Dagger", + "level": 1, + "price": "N/A", + "min dmg": 1, + "max dmg": 5 + }, + { + "item": " Iron Sword", + "level": 2, + "price": 100, + "min dmg": 2, + "max dmg": 6 + }, + { + "item": "Iron Mace", + "level": 3, + "price": 500, + "min dmg": 5, + "max dmg": 10 + }, + { + "item": "Iron Flail", + "level": 4, + "price": 750, + "min dmg": 7, + "max dmg": 12 + }, + { + "item": "Iron Battleaxe", + "level": 7, + "price": 2500, + "min dmg": 10, + "max dmg": 15 + }, + { + "item": "Golden Hammer", + "level": 10, + "price": 4500, + "min dmg": 13, + "max dmg": 18 + }, + { + "item": "Hammer of Gaia", + "level": 11, + "price": 7000, + "min dmg": 14, + "max dmg": 21 + }, + { + "item": "Enhanced Bow", + "level": 12, + "price": 9500, + "min dmg": 16, + "max dmg": 25 + }, + { + "item": "Claws", + "level": 12, + "price": 11000, + "min dmg": 18, + "max dmg": 25 + }, + { + "item": "Burning Super-Death Sword", + "level": 13, + "price": 13000, + "min dmg": 20, + "max dmg": 26 + }, + { + "item": "Sharpened Sickle", + "level": 14, + "price": 15500, + "min dmg": 27, + "max dmg": 31 + }, + { + "item": "Fancy Wand", + "level": 15, + "price": 19000, + "min dmg": 32, + "max dmg": 36 + }, + { + "item": "Damp Trident", + "level": 16, + "price": 22500, + "min dmg": 38, + "max dmg": 41 + }, + { + "item": "Bronze Gauntlets", + "level": 17, + "price": 26000, + "min dmg": 43, + "max dmg": 46 + }, + { + "item": "Scissorgloves", + "level": 18, + "price": 29500, + "min dmg": 48, + "max dmg": 52 + }, + { + "item": "Family Cutlass", + "level": 19, + "price": 32000, + "min dmg": 53, + "max dmg": 56 + }, + { + "item": "Magy's Scythe", + "level": 20, + "price": 35500, + "min dmg": 56, + "max dmg": 62 + }, + { + "item": "Ghostly Scimitar", + "level": 25, + "price": 49500, + "min dmg": 78, + "max dmg": 87 + }, + { + "item": "Hunters Sorrow", + "level": 30, + "price": 63500, + "min dmg": 100, + "max dmg": 112 + }, + { + "item": "Citrus Blade", + "level": 30, + "price": 66500, + "min dmg": 99, + "max dmg": 106 + }, + { + "item": "Peacemaker", + "level": 35, + "price": 79000, + "min dmg": 115, + "max dmg": 130 + }, + { + "item": "Orchid Sword", + "level": 40, + "price": 100000, + "min dmg": 132, + "max dmg": 144 + }, + { + "item": "Light Longbow of Mourning", + "level": 45, + "price": 121875, + "min dmg": 153, + "max dmg": 167 + }, + { + "item": "Conjurer's Foresight", + "level": 50, + "price": 143750, + "min dmg": 175, + "max dmg": 190 + }, + { + "item": "Fearsome Rust Axe of Hunting", + "level": 55, + "price": 165625, + "min dmg": 192, + "max dmg": 213 + }, + { + "item": "Beserk Stone Axe of Hunting", + "level": 60, + "price": 187500, + "min dmg": 209, + "max dmg": 236 + }, + { + "item": "Chivalic Rapier", + "level": 75, + "price": 200000, + "min dmg": 200, + "max dmg": 222 + }, + { + "item": "Flintlock Pistol", + "level": 80, + "price": 230000, + "min dmg": 245, + "max dmg": 270 + }, + { + "item": "Zangetsu", + "level": 90, + "price": 432432, + "min dmg": 119, + "max dmg": 461 + }, + { + "item": "Die", + "level": 91, + "price": 654321, + "min dmg": 0, + "max dmg": 600 + }, + { + "item": "Schwarz Sieben Prototype Mk III", + "level": 95, + "price": 255000, + "min dmg": 260, + "max dmg": 290 + }, + { + "item": "Force Mithril Greatsword", + "level": 120, + "price": 350000, + "min dmg": 310, + "max dmg": 360 + }, + { + "item": "Astral Rod", + "level": 142, + "price": 555555, + "min dmg": 377, + "max dmg": 433 + }, + { + "item": "Sadist's Scythe's Return", + "level": 156, + "price": 666666, + "min dmg": 449, + "max dmg": 541 + }, + { + "item": "Spear of Indra", + "level": 200, + "price": 1000000, + "min dmg": 600, + "max dmg": 666 + }, + { + "item": "Z-Saber", + "level": 250, + "price": 1600000, + "min dmg": 740, + "max dmg": 830 + }, + { + "item": "Iron Tomahawk", + "level": 300, + "price": 3050000, + "min dmg": 895, + "max dmg": 1019 + }, + { + "item": "Polished Obsidian Battle-axe", + "level": 350, + "price": 4500000, + "min dmg": 1050, + "max dmg": 1208 + }, + { + "item": "Engraved Silver Musket", + "level": 400, + "price": 6000000, + "min dmg": 1200, + "max dmg": 1389 + }, + { + "item": "Toast", + "level": 420, + "price": 8888888, + "min dmg": 855, + "max dmg": 1666 + }, + { + "item": "Menacing Bardiche of Bloodletting", + "level": 450, + "price": 7800000, + "min dmg": 1350, + "max dmg": 1550 + }, + { + "item": "Purple Blade Return", + "level": 500, + "price": 10000000, + "min dmg": 1500, + "max dmg": 1750 + }, + { + "item": "Elegant Spiked War Mace", + "level": 550, + "price": 12000000, + "min dmg": 1700, + "max dmg": 1981 + }, + { + "item": "Massive Greatsword of Striking", + "level": 600, + "price": 16000000, + "min dmg": 1950, + "max dmg": 2262 + }, + { + "item": "Crystal Flail", + "level": 650, + "price": 22222222, + "min dmg": 2222, + "max dmg": 2499 + }, + { + "item": "Titanium Axe", + "level": 700, + "price": 29876543, + "min dmg": 2444, + "max dmg": 2753 + }, + { + "item": "Crystalline Musket", + "level": 750, + "price": 37373737, + "min dmg": 2666, + "max dmg": 2963 + }, + { + "item": "Pitchfork", + "level": 800, + "price": 47777777, + "min dmg": 2888, + "max dmg": 3222 + }, + { + "item": "Machete", + "level": 850, + "price": 58642085, + "min dmg": 3111, + "max dmg": 3444 + }, + { + "item": "Claymore", + "level": 900, + "price": 70123456, + "min dmg": 3333, + "max dmg": 3693 + }, + { + "item": "Halberd", + "level": 950, + "price": 83333333, + "min dmg": 3555, + "max dmg": 3939 + }, + { + "item": "War Hammer", + "level": 1000, + "price": 100010001, + "min dmg": 3842, + "max dmg": 4242 + }, + { + "item": "Crystal Cutlass", + "level": 1500, + "price": 266776681, + "min dmg": 6712, + "max dmg": 7272 + } +] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f56ce23 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +discord.py +psycopg2-binary +requests \ No newline at end of file diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000..1935e97 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.6.6 diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..eea7d9e --- /dev/null +++ b/utils.py @@ -0,0 +1,74 @@ +import re +import json + +USERREGEX = re.compile(r'!(=)+\[ (.+)\'s Adventure \](=)+!') +HPSREGEX = re.compile(r'[0-9,]+/[0-9,]+') + + +def read_adv(content): + try: + hps = HPSREGEX.findall(content) + hp = hps[1].replace(',', '').split("/") + hppercent = int(hp[0]) / int(hp[1]) * 100 + usr = USERREGEX.search(content) + usr = usr.group(2) + return usr, hppercent + except Exception: + pass + + +STATSUSERRE = re.compile(r'!=+ \[(.+)\'s Stats\] =+!') +LEVELRE = re.compile(r'Level (\d+)') +WEAPRE = re.compile(r'Weapon: (.+)') +with open('items/weapons.json') as f: + WEAPONS = json.loads(f.read()) + + +def read_stats(content): + try: + user = STATSUSERRE.search(content).groups(0)[0] + weap = WEAPRE.search(content).groups(0)[0] + for i in WEAPONS: + if i['item'] == weap: + weap = i + break + level = LEVELRE.search(content).groups(0)[0] + for i in WEAPONS[::-1]: + if i['level'] <= int(level): + if i['level'] != weap['level']: + new_weap = i + break + else: + new_weap = None + break + if new_weap is not None: + out = f'```{user}\'s Weapon\n' \ + f'Name: {weap["item"]} Maxhit: '\ + f'{weap["max dmg"]} Minhit: {weap["min dmg"]}\n'\ + f'You should upgrade to {new_weap["item"]} ' \ + f'costing {new_weap["price"]}gold with ' \ + f'a max hit of {new_weap["max dmg"]} and a ' \ + f'min hit of {new_weap["min dmg"]}```' + else: + out = f'```{user}\'s Weapon\n' \ + f'Name: {weap["item"]} ' \ + f'Maxhit: {weap["max dmg"]} ' \ + f'Minhit: {weap["min dmg"]}\n' \ + f'Your weapon seems correct for your level```' + return out + except Exception: + pass + +LOC_USR_RE = re.compile(r'(.+)\'s location') +CLEAN_DEST_RE = re.compile(r'([A-Za-z \']+) - (\d+)s') + +def read_travel(title, fields, **kwargs): + if title.endswith('location') and fields[0]["name"] == "Kingdom": + rawdestinations = fields[1]["value"].split('\n') + desetinations = {i[0]: int(i[1]) + for i in [CLEAN_DEST_RE.search(i).groups() + for i in rawdestinations]} + user = LOC_USR_RE.search(title).groups(0)[0] + return user, desetinations + +DEST_RE = re.compile(r'started their journey to ([A-Za-z \']+)!')