summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore93
-rw-r--r--.gitmodules3
m---------joblib0
-rw-r--r--main.py177
-rw-r--r--requirements.txt11
5 files changed, 284 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a3401aa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,93 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+!/logs
+!run*.log
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# dotenv
+.env
+
+# virtualenv
+.venv/
+venv/
+ENV/
+
+# Spyder project settings
+.spyderproject
+
+# Rope project settings
+.ropeproject
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..52948d0
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "joblib"]
+ path = joblib
+ url = https://github.com/vainglorygame/joblib
diff --git a/joblib b/joblib
new file mode 160000
+Subproject 75c4caa934676f4addf633008d20137b45c15c8
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..c66891a
--- /dev/null
+++ b/main.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python3
+
+import logging
+import os
+import asyncio
+import asyncpg
+import discord
+from discord.ext import commands
+import joblib.joblib
+
+source_db = {
+ "host": os.environ.get("POSTGRESQL_SOURCE_HOST") or "localhost",
+ "port": os.environ.get("POSTGRESQL_SOURCE_PORT") or 5433,
+ "user": os.environ.get("POSTGRESQL_SOURCE_USER") or "vainraw",
+ "password": os.environ.get("POSTGRESQL_SOURCE_PASSWORD") or "vainraw",
+ "database": os.environ.get("POSTGRESQL_SOURCE_DB") or "vainsocial-raw"
+}
+
+dest_db = {
+ "host": os.environ.get("POSTGRESQL_DEST_HOST") or "localhost",
+ "port": os.environ.get("POSTGRESQL_DEST_PORT") or 5432,
+ "user": os.environ.get("POSTGRESQL_DEST_USER") or "vainweb",
+ "password": os.environ.get("POSTGRESQL_DEST_PASSWORD") or "vainweb",
+ "database": os.environ.get("POSTGRESQL_DEST_DB") or "vainsocial-web"
+}
+
+
+bot = commands.Bot(
+ command_prefix="!",
+ description="Vainsocial Vainglory stats bot")
+
+queue = None
+pool = None
+
+async def connect(queuedb, db):
+ logging.warning("connecting to database")
+
+ global queue
+ queue = joblib.joblib.JobQueue()
+ await queue.connect(**queuedb)
+ await queue.setup()
+
+ global pool
+ pool = await asyncpg.create_pool(**db)
+
+
+@bot.event
+async def on_ready():
+ logging.warning("Logged in as %s (" +
+ "https://discordapp.com/oauth2/authorize?&" +
+ "client_id=%s&scope=bot)",
+ bot.user.name, bot.user.id)
+ await connect(source_db, dest_db)
+
+
+@bot.event
+async def on_command_error(error, ctx):
+ if ctx.invoked_subcommand:
+ pages = bot.formatter.format_help_for(
+ ctx, ctx.invoked_subcommand)
+ for page in pages:
+ await bot.send_message(
+ ctx.message.channel, page)
+ else:
+ pages = bot.formatter.format_help_for(
+ ctx, ctx.command)
+ for page in pages:
+ await bot.send_message(
+ ctx.message.channel, page)
+
+
+@bot.command()
+async def vainsocial(region: str, name: str):
+ """Retrieves a player's stats."""
+ if region not in ["na", "eu", "sg", "ea", "sa"]:
+ await bot.say("That region is not supported.")
+ return
+
+ query = """
+ SELECT
+ player.name,
+ match.game_mode,
+ participant.hero, participant.winner,
+ participant.kills, participant.deaths, participant.assists, participant.farm,
+ participant.skill_tier, player.played, player.wins,
+ player.last_match_created_date
+ FROM match, roster, participant, player where
+ match.api_id=roster.match_api_id AND
+ roster.api_id=participant.roster_api_id AND
+ participant.player_api_id=player.api_id AND
+ player.name=$1 AND player.last_match_created_date<>$2
+ ORDER BY match.created_at DESC
+ LIMIT 1
+ """
+ def msg(dct):
+ tiers = ["Just Beginning", "Getting There", "Rock Solid", "Got Swagger", "Credible Threat", "The Hotness", "Simply Amazing", "Pinnacle Of Awesome", "Vainglorious"]
+ if data["skill_tier"] == -1:
+ tier = "Unranked"
+ else:
+ subtiers = ["Bronze", "Silver", "Gold"]
+ tier = tiers[data["skill_tier"]//3-1] + " " + subtiers[data["skill_tier"]%3]
+ modes = {
+ "blitz_pvp_ranked": "Blitz",
+ "casual_aral": "Battle Royale",
+ "private": "private casual",
+ "private_party_draft_match": "private draft",
+ "private_party_blitz_match": "private Blitz",
+ "private_party_aral_match": "private Battle Royale"
+ }
+ mode = modes.get(data["game_mode"]) or data["game_mode"]
+ heroes = {
+ "Sayoc": "Taka",
+ "Hero009": "Krul",
+ "Hero010": "Skaarf",
+ "Hero016": "Rona"
+ }
+ hero = heroes.get(data["hero"]) or data["hero"].replace("*", "")
+
+ return ("""
+%s: %s, %s wins / %s games
+Last match: %s %s as %s %s/%s/%s
+""") % (
+ data["name"], tier,
+ data["wins"], data["played"],
+ ("won" if data["winner"] else "lost"),
+ mode, hero,
+ data["kills"], data["deaths"], data["assists"]
+ )
+
+ async with pool.acquire() as con:
+ await bot.type()
+
+ data = await con.fetchrow(query, name, 'NULL')
+ if data is None:
+ in_cache = False # new user
+ lmcd = "2017-02-01T00:00:00Z"
+ msgid = await bot.say(
+ "%s: please wait…" % name)
+ else:
+ in_cache = True # returning user
+ lmcd = data["last_match_created_date"]
+ msgid = await bot.say(msg(data))
+
+ payload = {
+ "region": region,
+ "params": {
+ "filter[createdAt-start]": lmcd,
+ "filter[playerNames]": name
+ }
+ }
+ jobid = await queue.request(jobtype="grab",
+ payload=payload)
+
+ while True:
+ # wait for grab job to finish
+ status = await queue.status(jobid)
+ if status == 'finished':
+ break
+ if status == 'failed':
+ if not in_cache:
+ await bot.edit_message(
+ "Could not find you.")
+ return
+ asyncio.sleep(0.1)
+
+ while True:
+ # wait for processor to update the player
+ data = await con.fetchrow(query, name, lmcd)
+ if data is not None:
+ break
+ asyncio.sleep(0.1)
+
+ await bot.edit_message(msgid, msg(data))
+
+
+logging.basicConfig(level=logging.INFO)
+bot.run(os.environ["VAINSOCIAL_DISCORDTOKEN"])
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..7c3bc62
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,11 @@
+aiohttp==1.0.5
+appdirs==1.4.2
+async-timeout==1.1.0
+asyncpg==0.9.0
+chardet==2.3.0
+discord.py==0.16.7
+multidict==2.1.4
+packaging==16.8
+pyparsing==2.1.10
+six==1.10.0
+websockets==3.2