Skip to content

Commit

Permalink
Added config file and refactored some core features
Browse files Browse the repository at this point in the history
- Fixed missing format function in Redunda error logging
- Added a config file (closes #9)
- Refactored chat helper class to utils class and removed unneeded getters and setters (too much java recently, still damaged by that)
- Refactored ranks file
- Fixed announcing of the last flag rank (#11)
  • Loading branch information
Filnor committed Feb 21, 2018
1 parent bd969c0 commit 0d5f054
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 122 deletions.
102 changes: 50 additions & 52 deletions chat.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import getpass
import logging
import sys
import json
Expand All @@ -8,62 +7,59 @@

import flagbot.check_flags as check_flags
import flagbot.redunda as redunda
import config as config

from flagbot.chat_helper import chat_helper
from flagbot.utils import utils

from urllib.request import urlopen
from chatexchange.chatexchange.client import Client
from chatexchange.chatexchange.events import MessagePosted, MessageEdited

bot_parent = 'chade_'
bot_machine = 'HP Envy (dev machine)'
bot_version = 'v0.6.1'
rooms = {
"Debug": 163468,
"SOBotics": 111347,
"SOCVR": 41570
}
room_id = rooms["Debug"]
chat_helper = chat_helper(room_id)

utils = utils()

def main():
#Setup start actions
host_id = 'stackoverflow.com'
email = input("Email: ")
password = getpass.getpass("Password: ")

client = Client(host_id)
client.login(email, password)
chat_helper.setClient(client)
#Get config for the mode (debug/prod)
if input("Mode (use d dor debug, leave blank for productive): ") is "d":
print("Loading Debug config")
utils.config = config.debugConfig
else:
print("Loading Productive config")
utils.config = config.prodConfig

utils.roomNumber = utils.config["room"]

client = Client(utils.config["chatHost"])
client.login(utils.config["email"], utils.config["password"])
utils.client = client

room = client.get_room(room_id)
room = client.get_room(utils.config["room"])
room.join()
room.watch(on_message)
print(room.get_current_user_names())

quota_obj = json.loads(gzip.GzipFile(fileobj=io.BytesIO(urlopen("https://api.stackexchange.com/2.2/users/1?order=desc&sort=reputation&site=stackoverflow&key=K8pani4F)SeUn0QlbHQsbA((").read())).read().decode("utf-8"))
quota_obj = json.loads(gzip.GzipFile(fileobj=io.BytesIO(urlopen("https://api.stackexchange.com/2.2/users/1?order=desc&sort=reputation&site=stackoverflow&key={}".format(utils.config["stackExchangeApiKey"])).read())).read().decode("utf-8"))


if quota_obj['quota_remaining'] is not None:
chat_helper.setQuota(quota_obj['quota_remaining'])
utils.quota = quota_obj['quota_remaining']

logging.basicConfig(filename="CheckYerFlags.log", level=logging.INFO)
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))

logging.info("Joined room '{}' on {}".format(room.name, host_id))
logging.info("Joined room '{}' on {}".format(room.name, utils.config["chatHost"]))

#Run Redunda reporting on a seperate thread/in the background
stop_redunda = threading.Event()
redunda_thread = redunda.RedundaThread(stop_redunda, bot_version, logging)
redunda_thread = redunda.RedundaThread(stop_redunda, utils.config, logging)
redunda_thread.start()

while True:
message = input("<< ")
room.send_message(message)

client.logout()
stop_redunda.set()
#client.logout()
#stop_redunda.set()

def on_message(message, client):
if not isinstance(message, MessagePosted) and not isinstance(message, MessageEdited):
Expand All @@ -73,37 +69,39 @@ def on_message(message, client):

#Here are the responses defined
#region Default bot commands
if chat_helper.checkAliases(message_val, "alive"):
chat_helper.replyWith(message, "instance of {} is running on **{}/{}**".format(bot_version, bot_parent, bot_machine))
elif chat_helper.checkAliases(message_val, "say"):
chat_helper.postMessage(message.content.split('say', 1)[1])
elif chat_helper.checkAliases(message_val, "welcome"):
chat_helper.postMessage("Welcome to SOBotics! You can learn more about SOBotics and what we and [all the bots](https://sobotics.org/all-bots/) are doing here at our website, https://sobotics.org/. If you'd like to help out with flagging, reporting, or anything else, let us know! We have tons of [userscripts](https://sobotics.org/userscripts/) to make things easier, and you'll always find someone around who will help you to install them and explain how they work.")
elif chat_helper.checkAliases(message_val, "quota"):
chat_helper.postMessage("The remaining API quota is {}.".format(chat_helper.getQuota()))
elif chat_helper.checkAliases(message_val, "command") or chat_helper.checkAliases(message_val, "commands"):
chat_helper.replyWith(message, "You can find a list of my commands [here](https://github.com/SOBotics/FlaggersHall/wiki#commands)")
if utils.checkAliases(message_val, "alive"):
utils.replyWith(message, "instance of {} is running on **{}/{}**".format(utils.config["botVersion"], utils.config["botParent"], utils.config["botMachine"]))
elif utils.checkAliases(message_val, "v") or utils.checkAliases(message_val, "version"):
utils.replyWith(message, "Current version is {}".format(utils.config["botVersion"]))
elif utils.checkAliases(message_val, "say"):
utils.postMessage(message.content.split('say', 1)[1])
elif utils.checkAliases(message_val, "welcome"):
utils.postMessage("Welcome to SOBotics! You can learn more about SOBotics and what we and [all the bots](https://sobotics.org/all-bots/) are doing here at our website, https://sobotics.org/. If you'd like to help out with flagging, reporting, or anything else, let us know! We have tons of [userscripts](https://sobotics.org/userscripts/) to make things easier, and you'll always find someone around who will help you to install them and explain how they work.")
elif utils.checkAliases(message_val, "quota"):
utils.postMessage("The remaining API quota is {}.".format(utils.quota))
elif utils.checkAliases(message_val, "command") or utils.checkAliases(message_val, "commands"):
utils.replyWith(message, "You can find a list of my commands [here](https://github.com/SOBotics/FlaggersHall/wiki#commands)")
#endregion
elif chat_helper.checkAliases(message_val, "status mine"):
check_flags.checkOwnFlags(message, chat_helper)
elif chat_helper.checkAliases(message_val, "status"):
check_flags.checkFlags(message, chat_helper)
elif utils.checkAliases(message_val, "status mine"):
check_flags.checkOwnFlags(message, utils)
elif utils.checkAliases(message_val, "status"):
check_flags.checkFlags(message, utils)
#region Fun commands
elif message.content.startswith("🚂"):
chat_helper.postMessage("🚃")
elif chat_helper.checkAliases(message_val, "why"):
chat_helper.replyWith(message, "[Because of you](https://www.youtube.com/watch?v=Ra-Om7UMSJc)")
elif chat_helper.checkAliases(message_val, "good bot"):
chat_helper.replyWith(message, "Thank you")
elif chat_helper.checkAliases(message_val.lower(), "thanks") or chat_helper.checkAliases(message_val.lower(), "thank you") or chat_helper.checkAliases(message_val.lower(), "thx"):
chat_helper.replyWith(message, "You're welcome.")
elif chat_helper.checkAliases(message_val, "status Batty") or chat_helper.checkAliases(message_val, "status batty"):
chat_helper.replyWith(message, "Batty [can barely use links](https://chat.stackoverflow.com/rooms/111347/conversation/batty-learns-how-to-use-links). She's not worthy of a flag rank *kappa*")
utils.postMessage("🚃")
elif utils.checkAliases(message_val, "why"):
utils.replyWith(message, "[Because of you](https://www.youtube.com/watch?v=Ra-Om7UMSJc)")
elif utils.checkAliases(message_val, "good bot"):
utils.replyWith(message, "Thank you")
elif utils.checkAliases(message_val.lower(), "thanks") or utils.checkAliases(message_val.lower(), "thank you") or utils.checkAliases(message_val.lower(), "thx"):
utils.replyWith(message, "You're welcome.")
elif utils.checkAliases(message_val, "status Batty") or utils.checkAliases(message_val, "status batty"):
utils.replyWith(message, "Batty [can barely use links](https://chat.stackoverflow.com/rooms/111347/conversation/batty-learns-how-to-use-links). She's not worthy of a flag rank *kappa*")
elif "shrug" in message.content:
chat_helper.replyWith(message, "🤷")
utils.replyWith(message, "🤷")
# message.message.reply("¯\\ _(ツ)_/¯") This is the more ASCII-/Retro-version. Choose between the emoji and this one as you like
elif "kappa" in message.content and "gif" in message.content:
chat_helper.replyWith(message, "https://i.imgur.com/8TRbWHM.gif")
utils.replyWith(message, "https://i.imgur.com/8TRbWHM.gif")
#endregion


Expand Down
27 changes: 27 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
Is this file are the configurations for debug and production mode defined. In the debug config is also explained for what the properties are for
"""

debugConfig = {
"botParent": "chade_", # The person which is responsible for running the instance (e.g. you if you're running your own instance)
"botMachine": "HP Envy (dev machine)", # The machine the bot runs on. Try to give it something easy to identify (I use the manufacturer and model name of my machine for development, and the distribution name for the productive server)
"botVersion": "v0.7.0", # The current version of the Bot, be sure to read the wiki on how to increment the version
"room": 163468, # The ID for the chatroom we work with.
"chatHost": "stackoverflow.com", # The site where the bot runs. Please note that the bot has only been tested in rooms on stackoverflow.com
"email": "", # The credentials to log in a user which posts the messages.
"password": "", # WARNING: DO NOT COMMIT YOUR CHANGES IF YOUR CREDENTIALS ARE STILL IN HERE
"stackExchangeApiKey": "K8pani4F)SeUn0QlbHQsbA((", # If you want to run the bot productively on your own, you may request a API key for yourself.
"redundaKey": "19b558436329ff6eb8247bc21fdd2aaa1135597b5bb858a10e8eef2688b8565e" # If you want to have a key on your own, ping me so I so we can discuss the situation (because I may need to add you as collaborator in order to create a key that show the bot of with you as the owner)
}

prodConfig = {
"botParent": "chade_", # The person which is responsible for running the instance (e.g. you if you're running your own instance)
"botMachine": "UbuntuServer", # The machine the bot runs on. Try to give it something easy to identify (I use the manufacturer and model name of my machine for development, and the distribution name for the productive server)
"botVersion": "v0.7.0", # The current version of the Bot, be sure to read the wiki on how to increment the version
"room": 111347,
"chatHost": "stackoverflow.com", # The site where the bot runs. Please note that the bot has only been tested in rooms on stackoverflow.com
"email": "", # The credentials to log in a user which posts the messages.
"password": "", # WARNING: DO NOT COMMIT YOUR CHANGES IF YOUR CREDENTIALS ARE STILL IN HERE
"stackExchangeApiKey": "K8pani4F)SeUn0QlbHQsbA((", # If you want to run the bot productively on your own, you may request a API key for yourself.
"redundaKey": "1edfa96cf4edac49271759220c029a35c21fddda3e1fb28d82ec6bcba6dd6c7a" # If you want to have a key on your own, ping me so I so we can discuss the situation (because I may need to add you as collaborator in order to create a key that show the bot of with you as the owner)
}
27 changes: 13 additions & 14 deletions flagbot/check_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,27 @@
from pyquery import PyQuery as pq


def checkOwnFlags(message, chat_helper):
def checkOwnFlags(message, utils):
page = urlopen("https://stackoverflow.com/users/{}?tab=topactivity".format(message.user.id))
html = page.read().decode("utf-8")
jQuery = pq(html)
flagCount = str(pq(jQuery(".g-col.g-row.fl-none").children()[6]).html()).replace('\n', ' ').replace('\r', '').strip().strip(" helpful flags")
currentFlagRank = getCurrentFlagRank(int(flagCount.replace(",", "")))
chat_helper.replyWith(message, "You have {} helpful flags. Your last achieved rank was **{}** ({}) for {} helpful flags.".format(flagCount, currentFlagRank["title"], currentFlagRank["description"], currentFlagRank["count"]))
utils.replyWith(message, "You have {} helpful flags. Your last achieved rank was **{}** ({}) for {} helpful flags.".format(flagCount, currentFlagRank["title"], currentFlagRank["description"], currentFlagRank["count"]))
# message.message.reply("**This feature is not working yet!** You need [69] more helpful flags to get your next rank: **Burn the evil** (666 flags)") # original message, currently kept for historical reasons

def checkFlags(message, chat_helper):
def checkFlags(message, utils):
userId = ""
try:
userId = message.content.split('status ', 1)[1]
except IndexError:
pass

#region Validate the specified ID using the Stack Exchange API
validId = False
if userId.isdecimal():
# Now we call the Stack Exchange API to validate the user's id
response = urlopen("https://api.stackexchange.com/2.2/users/{}?order=desc&sort=reputation&site=stackoverflow&key=K8pani4F)SeUn0QlbHQsbA((".format(userId)).read()
response = urlopen("https://api.stackexchange.com/2.2/users/{}?order=desc&sort=reputation&site=stackoverflow&key={}".format(userId, utils.config["stackExchangeApiKey"])).read()
buffer = io.BytesIO(response)
gzipped_file = gzip.GzipFile(fileobj=buffer)
content = gzipped_file.read()
Expand All @@ -36,10 +37,11 @@ def checkFlags(message, chat_helper):
if len(data["items"]) is 1:
validId = True
if data['quota_remaining'] is not None:
chat_helper.setQuota(data['quota_remaining'])
utils.quota = data['quota_remaining']
else:
chat_helper.postMessage("The specfied argument for the user id is not correct. Only digits are allowed.")
utils.postMessage("The specfied argument for the user id is not correct. Only digits are allowed.")
return
#endregion

if validId:
page = urlopen("https://stackoverflow.com/users/{}?tab=topactivity".format(message.content.split('status ', 1)[1]))
Expand All @@ -50,16 +52,13 @@ def checkFlags(message, chat_helper):
userName = str(pq(jQuery(".name")[0]).html()).replace('\n', ' ').replace('\r', '').strip()
# Stripping mod markup from the name
userName = html_parser.strip_tags(userName)
chat_helper.postMessage("{} has {} helpful flags. Their last achieved rank was **{}** ({}) for {} helpful flags.".format(userName, flagCount, currentFlagRank["title"], currentFlagRank["description"], currentFlagRank["count"]))
utils.postMessage("{} has {} helpful flags. Their last achieved rank was **{}** ({}) for {} helpful flags.".format(userName, flagCount, currentFlagRank["title"], currentFlagRank["description"], currentFlagRank["count"]))
else:
chat_helper.postMessage("The specfied user id does not belong to an existing user.")
utils.postMessage("The specfied user id does not belong to an existing user.")

def getCurrentFlagRank(flagCount):
rankThresholds = []

differences = []
for rank in ranks.ranks:
rankThresholds.append(int(rank))
differences.append(flagCount - rank["count"])

for rank in rankThresholds:
if rank - flagCount >= 0:
return ranks.ranks[str(rankThresholds[rankThresholds.index(rank) - 1])]
return ranks.ranks[differences.index(min(i for i in differences if i > 0))]
76 changes: 38 additions & 38 deletions flagbot/ranks.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,62 @@
ranks = {
"365": {
ranks = [
{
"count": 365,
"title": "A flag a day keeps bad posts away",
"description": "One year has 365 days",
"count": 365
"description": "One year has 365 days"
},
"1111": {
{
"count": 1111,
"title": "No badge needed",
"description": "A number is prettier than a badge anyway!",
"count": 1111
"description": "A number is prettier than a badge anyway!"
},
"2008": {
{
"count": 2008,
"title": "Flag Overflow",
"description": "Stack Overflow was created in 2008",
"count": 2008
"description": "Stack Overflow was created in 2008"
},
"10000": {
{
"count": 10000,
"title": "Elite Squad",
"description": "At one point of time, there were only 16 of us",
"count": 10000
"description": "At one point of time, there were only 16 of us"
},
"11111": {
{
"count": 11111,
"title": "Game of Flags",
"description": "All elevens because the TV show Game of Thrones started in 2011",
"count": 11111
"description": "All elevens because the TV show Game of Thrones started in 2011"
},
"22656": {
{
"count": 22656,
"title": "Almost Jon Skeet",
"description": "22656 is his (John's) user ID",
"count": 22656
"description": "22656 is his (John's) user ID"
},
"33333": {
{
"count": 33333,
"title": "The Mad Flagger",
"description": "Got nothing better to do with your time? ;D",
"count": 33333
"description": "Got nothing better to do with your time? ;D"
},
"42195": {
{
"count": 42195,
"title": "The Marathon",
"description": "Marathon's length in meters",
"count": 42195
"description": "Marathon's length in meters"
},
"65536": {
{
"count": 65536,
"title": "The two to the sixteen",
"description": None,
"count": 65536
"description": None
},
"101010": {
{
"count": 101010,
"title": "Definitely a robot",
"description": "42 in binary code. [Also 42 is the Answer to the Ultimate Question of Life, the Universe, and Everything](https://en.wikipedia.org/wiki/Phrases_from_The_Hitchhiker%27s_Guide_to_the_Galaxy#Answer_to_the_Ultimate_Question_of_Life,_the_Universe,_and_Everything_(42))",
"count": 101010
"description": "42 in binary code. [Also 42 is the Answer to the Ultimate Question of Life, the Universe, and Everything](https://en.wikipedia.org/wiki/Phrases_from_The_Hitchhiker%27s_Guide_to_the_Galaxy#Answer_to_the_Ultimate_Question_of_Life,_the_Universe,_and_Everything_(42))"
},
"2147483647": {
{
"count": 2147483647,
"title": "The Overflow",
"description": "[Maximum size of a 32-bit integer](https://stackoverflow.com/a/94608)",
"count": 2147483647
"description": "[Maximum size of a 32-bit integer](https://stackoverflow.com/a/94608)"
},
"4294967296": {
{
"count": 4294967296,
"title": "A `long` journey",
"description": None,
"count": 4294967296
"description": None
}
}
]
8 changes: 4 additions & 4 deletions flagbot/redunda.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@


class RedundaThread(Thread):
def __init__(self, event, bot_version, logging):
def __init__(self, event, config, logging):
Thread.__init__(self)
self.stopped = event
self.bot_version = bot_version
self.config = config
self.logging = logging

def run(self):
Expand All @@ -19,12 +19,12 @@ def run(self):

def pingRedunda(self):
try:
data = parse.urlencode({"key": "19b558436329ff6eb8247bc21fdd2aaa1135597b5bb858a10e8eef2688b8565e", "version": self.bot_version}).encode()
data = parse.urlencode({"key": self.config["redundaKey"], "version": self.config["botVersion"]}).encode()
req = request.Request("https://redunda.sobotics.org/status.json", data)

response = request.urlopen(req)

jsonReturned = json.loads(response.read().decode("utf-8"))
except (OSError, URLError) as e:
current_timestamp = strftime("%Y-%m-%d %H:%M:%S", localtime())
self.logging.warn("Pinging Redunda failed at {} CET", current_timestamp)
self.logging.warn("Pinging Redunda failed at {} CET".format(current_timestamp))
Loading

0 comments on commit 0d5f054

Please sign in to comment.