Merge branch 'feature/refactor-project-structure'

master
Tristan Gosselin-Hane 6 years ago
commit 0a2068eb8b
No known key found for this signature in database
GPG Key ID: D2282BE1CF7B78DA
  1. 42
      .github/workflows/masterdockerimage.yml
  2. 18
      .github/workflows/pullrequest.yml
  3. 1
      .gitignore
  4. 2
      Dockerfile
  5. 8
      Pipfile
  6. 164
      Pipfile.lock
  7. 3
      minecraft-discord-bridge/__main__.py
  8. 8
      minecraft-discord-bridge/auth_server.py
  9. 1
      minecraft-discord-bridge/config.py
  10. 2
      minecraft-discord-bridge/database.py
  11. 0
      minecraft-discord-bridge/database_session.py
  12. 0
      minecraft-discord-bridge/elasticsearch_logger.py
  13. 55
      minecraft-discord-bridge/minecraft_discord_bridge.py
  14. 22
      setup.py
  15. 12
      tox.ini

@ -0,0 +1,42 @@
name: Docker Image
on:
push:
branches:
# Build only the master branch
- 'master'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v1
- name: Login to Docker Hub
uses: actions/docker/login@master
env:
DOCKER_USERNAME: starcraft66
DOCKER_PASSWORD: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Run tox
uses: tox-dev/gh-action-tox@master
env:
DOCKER_USERNAME: starcraft66
DOCKER_PASSWORD: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Login to the GitHub Package Registry
uses: actions/docker/login@master
env:
DOCKER_REGISTRY_URL: "docker.pkg.github.com"
DOCKER_USERNAME: starcraft66
DOCKER_PASSWORD: ${{ secrets.GITHUB_TOKEN_OWNED }}
- name: Build the master docker image
uses: actions/docker/cli@master
with:
args: "build -t starcraft66/minecraft-discord-bridge:latest -t docker.pkg.github.com/starcraft66/docker-minecraft-discord-bridge/minecraft-discord-bridge:latest ."
- name: Push the master docker image to Docker Hub
uses: actions/docker/cli@master
with:
args: "push starcraft66/minecraft-discord-bridge:latest"
- name: Push the master docker image to GitHub Package Registry
uses: actions/docker/cli@master
with:
args: "push docker.pkg.github.com/starcraft66/docker-minecraft-discord-bridge/minecraft-discord-bridge:latest"

@ -0,0 +1,18 @@
name: Test Pull Request
on:
push:
pull_request:
- '*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v1
- name: Run tox
uses: tox-dev/gh-action-tox@master
env:
DOCKER_USERNAME: starcraft66
DOCKER_PASSWORD: ${{ secrets.DOCKER_HUB_TOKEN }}

1
.gitignore vendored

@ -1,2 +1,3 @@
__pycache__/
config.json
db.sqlite

@ -12,4 +12,4 @@ RUN pip install --no-cache-dir pipenv \
VOLUME "/data"
CMD [ "python", "-m", "pipenv", "run", "./webhook-bridge.py" ]
CMD [ "python", "-m", "pipenv", "run", "python", "-m", "minecraft-discord-bridge" ]

@ -5,6 +5,9 @@ name = "pypi"
[dev-packages]
flake8 = "*"
pylint = "*"
tox = "*"
setuptools = "*"
[packages]
requests = "*"
@ -13,9 +16,6 @@ future = "*"
sqlalchemy = "*"
mcstatus = "*"
quarry = "*"
discord.py = "*"
discord = {py = "*"}
bidict = "*"
minecraft = {git = "https://github.com/ammaraskar/pyCraft.git"}
[requires]
python_version = "3.7"

164
Pipfile.lock generated

@ -1,12 +1,10 @@
{
"_meta": {
"hash": {
"sha256": "e28458bdbdc6803b201fc7be5b62db65056118fb1430d839b868c959167066f1"
"sha256": "9dc84aff633d8c6a344ee0e25946c2f0f57c2588d5daee523d15a95c24a5e537"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"requires": {},
"sources": [
{
"name": "pypi",
@ -450,6 +448,13 @@
}
},
"develop": {
"astroid": {
"hashes": [
"sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4",
"sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4"
],
"version": "==2.2.5"
},
"entrypoints": {
"hashes": [
"sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19",
@ -457,6 +462,13 @@
],
"version": "==0.3"
},
"filelock": {
"hashes": [
"sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59",
"sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"
],
"version": "==3.0.12"
},
"flake8": {
"hashes": [
"sha256:19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548",
@ -465,6 +477,44 @@
"index": "pypi",
"version": "==3.7.8"
},
"importlib-metadata": {
"hashes": [
"sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26",
"sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"
],
"markers": "python_version < '3.8'",
"version": "==0.23"
},
"isort": {
"hashes": [
"sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
"sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"
],
"version": "==4.3.21"
},
"lazy-object-proxy": {
"hashes": [
"sha256:02b260c8deb80db09325b99edf62ae344ce9bc64d68b7a634410b8e9a568edbf",
"sha256:18f9c401083a4ba6e162355873f906315332ea7035803d0fd8166051e3d402e3",
"sha256:1f2c6209a8917c525c1e2b55a716135ca4658a3042b5122d4e3413a4030c26ce",
"sha256:2f06d97f0ca0f414f6b707c974aaf8829c2292c1c497642f63824119d770226f",
"sha256:616c94f8176808f4018b39f9638080ed86f96b55370b5a9463b2ee5c926f6c5f",
"sha256:63b91e30ef47ef68a30f0c3c278fbfe9822319c15f34b7538a829515b84ca2a0",
"sha256:77b454f03860b844f758c5d5c6e5f18d27de899a3db367f4af06bec2e6013a8e",
"sha256:83fe27ba321e4cfac466178606147d3c0aa18e8087507caec78ed5a966a64905",
"sha256:84742532d39f72df959d237912344d8a1764c2d03fe58beba96a87bfa11a76d8",
"sha256:874ebf3caaf55a020aeb08acead813baf5a305927a71ce88c9377970fe7ad3c2",
"sha256:9f5caf2c7436d44f3cec97c2fa7791f8a675170badbfa86e1992ca1b84c37009",
"sha256:a0c8758d01fcdfe7ae8e4b4017b13552efa7f1197dd7358dc9da0576f9d0328a",
"sha256:a4def978d9d28cda2d960c279318d46b327632686d82b4917516c36d4c274512",
"sha256:ad4f4be843dace866af5fc142509e9b9817ca0c59342fdb176ab6ad552c927f5",
"sha256:ae33dd198f772f714420c5ab698ff05ff900150486c648d29951e9c70694338e",
"sha256:b4a2b782b8a8c5522ad35c93e04d60e2ba7f7dcb9271ec8e8c3e08239be6c7b4",
"sha256:c462eb33f6abca3b34cdedbe84d761f31a60b814e173b98ede3c81bb48967c4f",
"sha256:fd135b8d35dfdcdb984828c84d695937e58cc5f49e1c854eb311c4d6aa03f4f1"
],
"version": "==1.4.2"
},
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
@ -472,6 +522,34 @@
],
"version": "==0.6.1"
},
"more-itertools": {
"hashes": [
"sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832",
"sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"
],
"version": "==7.2.0"
},
"packaging": {
"hashes": [
"sha256:28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47",
"sha256:d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108"
],
"version": "==19.2"
},
"pluggy": {
"hashes": [
"sha256:0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6",
"sha256:fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"
],
"version": "==0.13.0"
},
"py": {
"hashes": [
"sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
"sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
],
"version": "==1.8.0"
},
"pycodestyle": {
"hashes": [
"sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
@ -485,6 +563,84 @@
"sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
],
"version": "==2.1.1"
},
"pylint": {
"hashes": [
"sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09",
"sha256:723e3db49555abaf9bf79dc474c6b9e2935ad82230b10c1138a71ea41ac0fff1"
],
"index": "pypi",
"version": "==2.3.1"
},
"pyparsing": {
"hashes": [
"sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80",
"sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"
],
"version": "==2.4.2"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
},
"toml": {
"hashes": [
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
],
"version": "==0.10.0"
},
"tox": {
"hashes": [
"sha256:0bc216b6a2e6afe764476b4a07edf2c1dab99ed82bb146a1130b2e828f5bff5e",
"sha256:c4f6b319c20ba4913dbfe71ebfd14ff95d1853c4231493608182f66e566ecfe1"
],
"index": "pypi",
"version": "==3.14.0"
},
"typed-ast": {
"hashes": [
"sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e",
"sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e",
"sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0",
"sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c",
"sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631",
"sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4",
"sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34",
"sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b",
"sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a",
"sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233",
"sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1",
"sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36",
"sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d",
"sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
"sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
],
"markers": "implementation_name == 'cpython'",
"version": "==1.4.0"
},
"virtualenv": {
"hashes": [
"sha256:680af46846662bb38c5504b78bad9ed9e4f3ba2d54f54ba42494fdf94337fe30",
"sha256:f78d81b62d3147396ac33fc9d77579ddc42cc2a98dd9ea38886f616b33bc7fb2"
],
"version": "==16.7.5"
},
"wrapt": {
"hashes": [
"sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1"
],
"version": "==1.11.2"
},
"zipp": {
"hashes": [
"sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e",
"sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"
],
"version": "==0.6.0"
}
}
}

@ -0,0 +1,3 @@
if __name__ == "__main__":
from . import minecraft_discord_bridge
minecraft_discord_bridge.main()

@ -1,9 +1,9 @@
from datetime import datetime, timezone
from datetime import datetime
from quarry.net.server import ServerFactory, ServerProtocol
from database import AccountLinkToken, MinecraftAccount, DiscordAccount
import database_session
from .database import AccountLinkToken, MinecraftAccount, DiscordAccount
from . import database_session
class AuthProtocol(ServerProtocol):
@ -47,7 +47,7 @@ class AuthProtocol(ServerProtocol):
return
if datetime.utcnow() < token.expiry:
# Check if they already have a linked account and are re-linking
if discord_account.minecraft_account_id != None:
if discord_account.minecraft_account_id is not None:
existing_account = session.query(MinecraftAccount).filter_by(
id=discord_account.minecraft_account_id).first()
self.logger.info("unlinking existing {} account and replacing it with {}".format(

@ -3,6 +3,7 @@ import logging
log = logging.getLogger("bridge.config")
class Configuration(object):
def __init__(self, path):
try:

@ -3,7 +3,7 @@ from datetime import datetime, timedelta, timezone
from sqlalchemy import Column, String, Integer, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from database_session import Base
from .database_session import Base
class DiscordChannel(Base):

@ -14,12 +14,12 @@ import random
import string
import uuid
from threading import Thread
from config import Configuration
from database import DiscordChannel, AccountLinkToken, DiscordAccount
import database_session
from .config import Configuration
from .database import DiscordChannel, AccountLinkToken, DiscordAccount
from . import database_session
from datetime import datetime, timedelta, timezone
import elasticsearch_logger as el
from . import elasticsearch_logger as el
from minecraft import authentication
from minecraft.exceptions import YggdrasilError
from minecraft.networking.connection import Connection
@ -75,7 +75,7 @@ def mc_username_to_uuid(username):
long_uuid = uuid.UUID(player_uuid)
UUID_CACHE.inv[username] = str(long_uuid)
return player_uuid
except:
except requests.RequestException:
log.error("Failed to lookup {}'s UUID using the Mojang API.".format(username))
else:
return UUID_CACHE.inv[username]
@ -129,7 +129,7 @@ def setup_logging(level):
log_level = logging.INFO
log_format = "%(asctime)s:%(name)s:%(levelname)s:%(message)s"
logging.basicConfig(filename="bridge_log.log", format=log_format, level=log_level)
stdout_logger=logging.StreamHandler(sys.stdout)
stdout_logger = logging.StreamHandler(sys.stdout)
stdout_logger.setFormatter(logging.Formatter(log_format))
logging.getLogger().addHandler(stdout_logger)
@ -138,7 +138,7 @@ def run_auth_server(port):
# We need to import twisted after setting up the logger because twisted hijacks our logging
# TODO: Fix this in a cleaner way
from twisted.internet import reactor
from auth_server import AuthFactory
from .auth_server import AuthFactory
# Create factory
factory = AuthFactory()
@ -289,12 +289,12 @@ def main():
if ACCEPT_JOIN_EVENTS:
webhook_payload = {
'username': username,
'avatar_url': "https://visage.surgeplay.com/face/160/{}".format(player_uuid),
'avatar_url': "https://visage.surgeplay.com/face/160/{}".format(player_uuid),
'content': '',
'embeds': [{'color': 65280, 'title': '**Joined the game**'}]
}
for webhook in WEBHOOKS:
post = requests.post(webhook,json=webhook_payload)
requests.post(webhook, json=webhook_payload)
if config.es_enabled:
el.log_connection(
uuid=action.uuid, reason=el.ConnectionReason.CONNECTED, count=len(PLAYER_LIST))
@ -305,9 +305,9 @@ def main():
ACCEPT_JOIN_EVENTS = True
if config.es_enabled:
diff = set(PREVIOUS_PLAYER_LIST.keys()) - set(PLAYER_LIST.keys())
for idx, uuid in enumerate(diff):
el.log_connection(uuid=uuid, reason=el.ConnectionReason.DISCONNECTED,
count=len(PREVIOUS_PLAYER_LIST) - (idx + 1))
for idx, player_uuid in enumerate(diff):
el.log_connection(uuid=player_uuid, reason=el.ConnectionReason.DISCONNECTED,
count=len(PREVIOUS_PLAYER_LIST) - (idx + 1))
# Don't bother announcing the bot's own join message (who cares) but log it for analytics still
if config.es_enabled:
el.log_connection(
@ -321,12 +321,12 @@ def main():
player_uuid = action.uuid
webhook_payload = {
'username': username,
'avatar_url': "https://visage.surgeplay.com/face/160/{}".format(player_uuid),
'avatar_url': "https://visage.surgeplay.com/face/160/{}".format(player_uuid),
'content': '',
'embeds': [{'color': 16711680, 'title': '**Left the game**'}]
}
for webhook in WEBHOOKS:
post = requests.post(webhook,json=webhook_payload)
requests.post(webhook, json=webhook_payload)
del UUID_CACHE[action.uuid]
del PLAYER_LIST[action.uuid]
if config.es_enabled:
@ -346,7 +346,7 @@ def main():
chat_string += chat_component["text"]
# Handle chat message
regexp_match = re.match("<(.*?)> (.*)", chat_string, re.M|re.I)
regexp_match = re.match("<(.*?)> (.*)", chat_string, re.M | re.I)
if regexp_match:
username = regexp_match.group(1)
original_message = regexp_match.group(2)
@ -369,11 +369,11 @@ def main():
message = escape_markdown(remove_emoji(original_message.strip().replace("@", "@\N{zero width space}")))
webhook_payload = {
'username': username,
'avatar_url': "https://visage.surgeplay.com/face/160/{}".format(player_uuid),
'avatar_url': "https://visage.surgeplay.com/face/160/{}".format(player_uuid),
'content': '{}'.format(message)
}
for webhook in WEBHOOKS:
post = requests.post(webhook, json=webhook_payload)
requests.post(webhook, json=webhook_payload)
if config.es_enabled:
el.log_chat_message(
uuid=player_uuid, display_name=username, message=original_message, message_unformatted=chat_string)
@ -405,7 +405,7 @@ def main():
channel_webhooks = await discord_channel.webhooks()
found = False
for webhook in channel_webhooks:
if webhook.name == "_minecraft":
if webhook.name == "_minecraft" and webhook.user == discord_bot.user:
WEBHOOKS.append(webhook.url)
found = True
log.debug("Found webhook {} in channel {}".format(webhook.name, discord_channel.name))
@ -545,16 +545,20 @@ def main():
deleted = session.query(DiscordChannel).filter_by(channel_id=this_channel).delete()
session.commit()
session.close()
for webhook in message.channel:
if webhook.name == "_minecraft":
del WEBHOOKS[webhook.url]
for webhook in await message.channel.webhooks():
if webhook.name == "_minecraft" and webhook.user == discord_bot.user:
# Copy the list to avoid some problems since
# we're deleting indicies form it as we loop
# through it
if webhook.url in WEBHOOKS[:]:
WEBHOOKS.remove(webhook.url)
await webhook.delete()
if deleted < 1:
msg = "The bot was not chatting here!"
await message.channel.send(msg)
return
else:
msg = "The bot will no longer here!"
msg = "The bot will no longer chat here!"
await message.channel.send(msg)
return
@ -663,7 +667,8 @@ def main():
PREVIOUS_MESSAGE = message_to_send
NEXT_MESSAGE_TIME = datetime.now(timezone.utc) + timedelta(seconds=config.message_delay)
log.info("Outgoing message from discord: Username: {} Message: {}".format(minecraft_username, message_to_send))
log.info("Outgoing message from discord: Username: {} Message: {}".format(
minecraft_username, message_to_send))
for channel in channels:
webhooks = await discord_bot.get_channel(channel.channel_id).webhooks()
@ -685,8 +690,8 @@ def main():
if not dm_channel:
await message.author.create_dm()
send_channel = message.author.dm_channel
msg = "Unable to send chat message: there is no Minecraft account linked to this discord account," \
"please run `mc!register`."
msg = "Unable to send chat message: there is no Minecraft account linked to this discord " \
"account, please run `mc!register`."
await send_channel.send(msg)
except discord.errors.Forbidden:
if isinstance(message.author, discord.abc.User):

@ -0,0 +1,22 @@
import setuptools
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name="minecraft-discord-bridge",
version="0.0.1",
author="Tristan Gosselin-Hane",
author_email="starcraft66@gmail.com",
description="A bridge between minecraft and discord chat",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/starcraft66/minecraft-discord-bridge",
packages=setuptools.find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
],
python_requires='>=3.5',
)

@ -0,0 +1,12 @@
[tox]
envlist = flake8
[flake8]
max-line-length = 120
[testenv:flake8]
deps =
pipenv
commands =
pipenv install --dev
flake8
Loading…
Cancel
Save