diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..420b4ec --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [DMcP89] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.gitignore b/.gitignore index f535602..9403da1 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ guilds.json answers.txt *.pem .python-version +*.db \ No newline at end of file diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..77d811a --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: make run diff --git a/app.json b/app.json new file mode 100644 index 0000000..5826abb --- /dev/null +++ b/app.json @@ -0,0 +1,33 @@ +{ + "name": "Harambot", + "description": "A Yahoo Fantasy sports bot for Discord.", + "keywords": [ + "fantasy football", + "discord", + "yahoo", + "chat bot", + "ff", + "harambe" + ], + "website" : "https://github.com/DMcP89/harambot", + "repository": "https://github.com/DMcP89/harambot", + "env": { + "HARAMBOT_YAHOO_KEY" : { + "description": "Yahoo Consumer Key", + "value": "", + "required": "true" + }, + "HARAMBOT_YAHOO_SECRET" : { + "description": "Yahoo Consumer Secret", + "value": "", + "required": "true" + }, + "HARAMBOT_DISCORD_TOKEN" : { + "description": "Discord bot token", + "value": "", + "required": "true" + } + } + + +} diff --git a/config/settings.toml b/config/settings.toml index 76bb83a..89368aa 100644 --- a/config/settings.toml +++ b/config/settings.toml @@ -1,3 +1,5 @@ [default] LOGLEVEL = "DEBUG" -GUILDS_DATASTORE_LOC = "config/guilds.json" \ No newline at end of file +# Supported databases are sqlite, mysql, and postgres +GUILDS_DATASTORE_TYPE = "sqlite" +GUILDS_DATASTORE_LOC = "harambot.db" \ No newline at end of file diff --git a/harambot/bot.py b/harambot/bot.py new file mode 100644 index 0000000..111c026 --- /dev/null +++ b/harambot/bot.py @@ -0,0 +1,81 @@ +import os + +import logging +import discord +import requests +import base64 +import time +from yahoo_fantasy_api import game +from yahoo_oauth import OAuth2 + + +from discord.ext import commands +from cogs.meta import Meta +from cogs.misc import Misc +from cogs.yahoo import Yahoo +from config import settings +from database.models import Guild + +#logging.basicConfig(level=logging.INFO) +logger = logging.getLogger('harambot.py') +logger.setLevel(settings.loglevel) + +intents = discord.Intents.default() +intents.members = True + +bot = commands.Bot(command_prefix="$", description="", intents=intents) +bot.remove_command('help') + + + +@bot.event +async def on_ready(): + logger.info("Everything's all ready to go~") + +@bot.event +async def on_guild_join(guild): + logger.info("Joined {}".format(guild.name)) + if not Guild.select().where(Guild.guild_id == str(guild.id)).exists(): + await configure_guild(guild.owner, guild.id) + logger.info("Guild not configured!") + + +async def configure_guild(owner, id): + + def check(m): + return m.author == owner + + await owner.send("Thank you for adding Harambot to your server!") + await owner.send("Please open the following link to authorize with Yahoo, respond with the code given after authorization") + await owner.send("https://api.login.yahoo.com/oauth2/request_auth?redirect_uri=oob&response_type=code&client_id={}".format(settings.yahoo_key)) + code = await bot.wait_for('message', timeout=60, check=check) + encoded_creds = base64.b64encode(('{0}:{1}'.format(settings.yahoo_key, settings.yahoo_secret )).encode('utf-8')) + details = requests.post( + url='https://api.login.yahoo.com/oauth2/get_token', + data={"code": code.clean_content, 'redirect_uri': 'oob', 'grant_type': 'authorization_code'}, + headers = { + 'Authorization': 'Basic {0}'.format(encoded_creds.decode('utf-8')), + 'Content-Type': 'application/x-www-form-urlencoded' + } + ).json() + details['token_time'] = time.time() + await owner.send("Enter Yahoo League ID") + leauge_id = await bot.wait_for('message', timeout=60, check=check) + await owner.send("Enter Yahoo League Type(nfl, nhl, nba, mlb)") + leauge_type = await bot.wait_for('message', timeout=60, check=check) + await owner.send("Enter text to use with $RIP command") + RIP_text = await bot.wait_for('message', timeout=60, check=check) + await owner.send("Enter image url to use with $RIP command") + RIP_image_url = await bot.wait_for('message', timeout=60, check=check) + details["league_id"] = leauge_id.clean_content + details["league_type"] = leauge_type.clean_content + details["RIP_text"] = RIP_text.clean_content + details["RIP_image_url"] = RIP_image_url.clean_content + Guild.create(guild_id=id,**details) + return + +bot.add_cog(Meta(bot)) +bot.add_cog(Yahoo(bot, settings.yahoo_key, settings.yahoo_secret)) +bot.add_cog(Misc(bot)) + +bot.run(settings.discord_token, bot=True, reconnect=True) # Where 'TOKEN' is your bot token \ No newline at end of file diff --git a/harambot/cogs/meta.py b/harambot/cogs/meta.py index 8af0695..53b148c 100644 --- a/harambot/cogs/meta.py +++ b/harambot/cogs/meta.py @@ -18,7 +18,7 @@ async def help(self, ctx): embed.add_field(name="$RIP", value="Pay respects to Harambe", inline=False) embed.add_field(name="$standings", value="Returns the current standings of your league", inline=False) embed.add_field(name="$roster team_name", value="Returns the roster of the given team", inline=False) - embed.add_field(name="$player_details player_name", value="Returns the details of the given player", inline=False) + embed.add_field(name="$stats player_name", value="Returns the details of the given player", inline=False) embed.add_field(name="$trade", value="Create poll for latest trade for league approval", inline=False) embed.add_field(name="$matchups", value="Returns the current weeks matchups", inline=False) await ctx.send(embed=embed) diff --git a/harambot/cogs/misc.py b/harambot/cogs/misc.py index 6fe1eb7..83a8424 100644 --- a/harambot/cogs/misc.py +++ b/harambot/cogs/misc.py @@ -4,22 +4,23 @@ import discord import logging +from database.models import Guild + logger = logging.getLogger(__file__) logger.setLevel(logging.INFO) class Misc(commands.Cog): - def __init__(self, bot, guilds): + def __init__(self, bot): self.bot = bot - self.guilds = guilds @commands.command("RIP") async def RIP(self, ctx, *args): logger.info("RIP called") - league_details = self.guilds.getGuildDetails(ctx.guild.id) + guild = Guild.get(Guild.guild_id == str(ctx.guild.id)) respected = args[0] if args else "Harambe" - message = league_details["RIP_text"] +" "+ respected + message = guild.RIP_text +" "+ respected embed = discord.Embed(title="", description='', color=0xeee657) - embed.set_image(url=league_details["RIP_image_url"]) + embed.set_image(url=guild.RIP_image_url) await ctx.send(content=message,embed=embed) diff --git a/harambot/cogs/yahoo.py b/harambot/cogs/yahoo.py index e99fa03..2cfc6e0 100644 --- a/harambot/cogs/yahoo.py +++ b/harambot/cogs/yahoo.py @@ -1,6 +1,7 @@ from discord import embeds from discord.ext import commands from yahoo_oauth import OAuth2 +from playhouse.shortcuts import model_to_dict import discord @@ -8,6 +9,7 @@ import urllib3 import yahoo_api +from database.models import Guild logger = logging.getLogger(__file__) @@ -18,8 +20,8 @@ def oauth(func): async def setup(cog, ctx, *, content=None): - league_details = cog.guilds.getGuildDetails(ctx.guild.id) - cog.yahoo_api = yahoo_api.Yahoo(OAuth2(cog.KEY, cog.SECRET, **league_details), league_details["league_id"], league_details["league_type"]) + guild = Guild.get(Guild.guild_id == str(ctx.guild.id)) + cog.yahoo_api = yahoo_api.Yahoo(OAuth2(cog.KEY, cog.SECRET, **model_to_dict(guild)), guild.league_id, guild.league_type) if content: await func(cog, ctx, content=content) else: @@ -31,12 +33,11 @@ class Yahoo(commands.Cog): error_message = "I'm having trouble getting that right now please try again later" - def __init__(self, bot, KEY, SECRET, guilds): + def __init__(self, bot, KEY, SECRET): self.bot = bot self.http = urllib3.PoolManager() self.KEY = KEY self.SECRET = SECRET - self.guilds = guilds self.yahoo_api = None @@ -112,9 +113,9 @@ async def trade(self, ctx): await msg.add_reaction(no_emoji) - @commands.command("player_details") + @commands.command("stats") @oauth - async def player_details(self, ctx, *, content:str): + async def stats(self, ctx, *, content:str): logger.info("player_details called") details = self.yahoo_api.get_player_details(content) if details: diff --git a/harambot/config.py b/harambot/config.py index c697ef3..6c0a013 100644 --- a/harambot/config.py +++ b/harambot/config.py @@ -2,9 +2,7 @@ from dynaconf import Dynaconf settings = Dynaconf( - envvar_prefix="DYNACONF", + envvar_prefix="HARAMBOT", settings_files=['settings.toml', '.secrets.toml'], environments=True, -) - - +) \ No newline at end of file diff --git a/harambot/database/databasetype.py b/harambot/database/databasetype.py new file mode 100644 index 0000000..ce55f9c --- /dev/null +++ b/harambot/database/databasetype.py @@ -0,0 +1,6 @@ +from enum import Enum + +class DatabaseType(Enum): + SQLITE = 'sqlite' + MYSQL = 'mysql' + POSTGRES = 'postgres' \ No newline at end of file diff --git a/harambot/database/models.py b/harambot/database/models.py new file mode 100644 index 0000000..b6ec02c --- /dev/null +++ b/harambot/database/models.py @@ -0,0 +1,34 @@ +from peewee import * +from config import settings +from database.databasetype import DatabaseType + +if settings.guilds_datastore_type == DatabaseType.POSTGRES: + database = PostgresqlDatabase(settings.guild_db,user=settings.guild_db_user, password=settings.guild_db_pass, + host=settings.guild_db_host, port=settings.guild_db_port) +elif settings.guilds_datastore_type == DatabaseType.MYSQL: + database = MySQLDatabase(settings.guild_db,user=settings.guild_db_user, password=settings.guild_db_pass, + host=settings.guild_db_host, port=settings.guild_db_port) +elif settings.guilds_datastore_type == DatabaseType.SQLITE: + database = SqliteDatabase(settings.guilds_datastore_loc) +else: + database = SqliteDatabase(':memory:') + + +class BaseModel(Model): + class Meta: + database = database + + +class Guild(BaseModel): + guild_id = TextField(unique=True) + access_token = TextField() + refresh_token = TextField() + expires_in = IntegerField() + token_type = TextField() + xoauth_yahoo_guid = TextField() + token_time = BigIntegerField() + league_id = TextField() + league_type = TextField() + RIP_text = TextField() + RIP_image_url = TextField() + diff --git a/harambot/datastore.py b/harambot/datastore.py deleted file mode 100644 index f49724f..0000000 --- a/harambot/datastore.py +++ /dev/null @@ -1,24 +0,0 @@ -import logging -import time -import json - - -logger = logging.getLogger() -logger.setLevel(logging.INFO) -logging.disable(logging.DEBUG) - - -class GuildsDatastore(): - - def __init__(self, path_to_datastore): - self.path_to_datastore = path_to_datastore - self.refreshDatastore() - - def getGuildDetails(self,guild_id): - return self.guilds[str(guild_id)] - - def refreshDatastore(self): - with open(self.path_to_datastore, 'r') as f: - self.guilds = json.load(f) - f.close() - self.last_refresh_timestamp = time.time() \ No newline at end of file diff --git a/harambot/harambot.py b/harambot/harambot.py deleted file mode 100644 index e01029f..0000000 --- a/harambot/harambot.py +++ /dev/null @@ -1,38 +0,0 @@ -import os - -import logging - - -from discord.ext import commands -from cogs.meta import Meta -from cogs.misc import Misc -from cogs.yahoo import Yahoo -from datastore import GuildsDatastore -from config import settings - - -#logging.basicConfig(level=logging.INFO) -logger = logging.getLogger('harambot.py') -logger.setLevel(settings.loglevel) - - -bot = commands.Bot(command_prefix="$", description="") -bot.remove_command('help') - -@bot.event -async def on_ready(): - logger.info("Everything's all ready to go~") - -@bot.event -async def on_guild_join(guild): - logger.info("Joined {}".format(guild.name)) - -bot.add_cog(Meta(bot)) - - -guilds = GuildsDatastore(settings.guilds_datastore_loc) - -bot.add_cog(Yahoo(bot, settings.yahoo_key, settings.yahoo_secret, guilds)) -bot.add_cog(Misc(bot, guilds)) - -bot.run(settings.discord_token, bot=True, reconnect=True) # Where 'TOKEN' is your bot token diff --git a/requirements.txt b/requirements.txt index f7ddd40..a414ca2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -38,3 +38,4 @@ yahoo-fantasy-api==2.4.0 yahoo-oauth==1.1 yarl==1.3.0 zipp==3.4.0 +peewee==3.15.1 \ No newline at end of file diff --git a/tests/test-guilds.json b/tests/test-guilds.json deleted file mode 100644 index 66ed34d..0000000 --- a/tests/test-guilds.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "1234567890": { - "access_token": "test_access_token", - "guid": "test_guid", - "refresh_token": "test_refresh_token", - "token_time": 0.0, - "token_type": "bearer", - "league_id": "123456" - } -} \ No newline at end of file diff --git a/tests/test_datastore.py b/tests/test_datastore.py deleted file mode 100644 index 1f1361a..0000000 --- a/tests/test_datastore.py +++ /dev/null @@ -1,26 +0,0 @@ -import pytest -from harambot.datastore import GuildsDatastore - - -@pytest.fixture -def guilds_datastore(): - return GuildsDatastore("tests/test-guilds.json") - -def test_init(guilds_datastore): - assert guilds_datastore is not None - -def test_getGuildDetails(guilds_datastore): - guild_details = guilds_datastore.getGuildDetails("1234567890") - assert guild_details is not None - assert guild_details["access_token"] == "test_access_token" - assert guild_details["guid"] == "test_guid" - assert guild_details["refresh_token"] == "test_refresh_token" - assert guild_details["token_time"] == 0.0 - assert guild_details["token_type"] == "bearer" - assert guild_details["league_id"] == "123456" - - -def test_refreshDatastore(guilds_datastore): - previous_refresh = guilds_datastore.last_refresh_timestamp - guilds_datastore.refreshDatastore() - assert guilds_datastore.last_refresh_timestamp > previous_refresh \ No newline at end of file