Fandom

Zammy Wiki

TyBot/GEMWbot/gemw.py

< TyBot | GEMWbot

2,249pages on
this wiki
Add New Page
Talk0 Share

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.

# !/usr/bin/python
# -*- coding: utf-8 -*-
 
#from __future__ import unicode_literals # This solves a bunch of python2-problems
import ast
import getpass
import chatbot
import collections
import gemwdata
import irclib
import json
import os
import re
import subprocess
import sys
import threading
import time
import urllib3
import yaml
from datetime import datetime, timedelta
#from gemwdata import itemsdict
from sys import exit
from volumes import getVols
from wikibot import WikiBot
global api
 
class ChatBot(chatbot.ChatBot):
 
    def __init__(self, username, password, site):
        chatbot.ChatBot.__init__(self,
                                username,
                                password,
                                site)
        self.last_updated = time.time()
        self.new_day = False
        self.logger_on = False
        self.mods = yaml.load(open("mods.yaml", "r"))["mods"]
        self.ignored_users = []
 
    def on_join(self, c, e):
        if (self.logger_on):
            f = open('logs.txt','a')
            f.write(self.format_message(user=e.user.encode('ascii','ignore'),event='join'))
            f.close()
       # if (e.user == "RSChatBot"):
        #    c.send("RSChatbot detected back in chat. Updating logs and shutting off logger.")
         #   self.update_logs()
          #  self.logger_on = False
 
    def on_leave(self, c, e):
       # if (e.user == "RSChatBot"):
           # self.logger_on = True
           # self.last_updated = time.time()
           # c.send("RSChatBot has left. Backup logger has been turned on.")
        if (self.logger_on):
            f = open('logs.txt', 'a')
            f.write(self.format_message(user=e.user.encode('ascii','ignore'),event='leave'))
            f.close()
 
    def on_message(self, c, e):
        global on
        global is_running
        global pagesUpdated
        global amountOfPages
        """
        c - connection/client
        e - event
        """
#        print "%s: %s" % (e.user, e.text)
        if (self.logger_on):
            f = open('logs.txt','a')
            f.write(self.format_message(user=e.user.encode('ascii','ignore'),
                                        text=e.text.encode('ascii','ignore'),event='message'))
            f.close()
 
        if (e.user in self.ignored_users):
            return
 
        if (e.text.startswith( "~ignore " )) and (e.user in self.mods):
            ignoreduser = e.text.split(' ', 1)[1]
            if (ignoreduser in self.mods):
                c.send("I cannot ignore a chat mod.")
            else:
                self.ignored_users.append(ignoreduser)
                c.send("Ignoring " + ignoreduser + "!")
 
        if (e.text.startswith( "~unignore " )) and (e.user in self.mods):
            target = e.text.split(' ', 1)[1]
            if (target in self.ignored_users):
                self.ignored_users.remove(target)
                c.send("Unignoring " + target + "!")
 
        if  (e.text == "~die") and (e.user == "TyA"):
            on = False
            c.disconnect(nodisconnect=False)
        elif e.text == "~status":
            if (is_running == True):
                c.send("The GE Updater is running, on item %d / %d" % (pagesUpdated, amountOfPages))
            else:
                c.send("The GE Updater is not running!")
 
        elif e.text.startswith("~report "):
            reportee = e.text.split(" ", 1)[1]
            reportee = reportee[0].upper() + reportee[1:]
            c.send("Generating an edit report for " + reportee + " at [[User:TyBot/editreports/" + reportee + "]]")
            proc = subprocess.Popen("php tybot.editreports.php  '" + reportee + "'", shell=True, stdout=subprocess.PIPE)
 
        elif e.text == "~upsrc":
            proc = subprocess.Popen("php tybot.updateScript.php -f gemw.py -l TyBot/GEMWbot/gemw.py -e python", shell=True, stdout=subprocess.PIPE)
            c.send("Updated gemw.py at [[w:c:ty:TyBot/GEMWbot/gemw.py]]!")
        elif e.text == "~test":
            if e.user == "Hairr":
                c.send("Hairr: I love you more than everyone here. <3")
            elif e.user == "TyA":
                c.send(":D")
            elif e.user == "The Mol Man":
                c.send("/)")
            elif e.user == "Spineweilder":
                c.send("Please don't weild my spine!")
            elif e.user == "Haidro":
                c.send("Hai dro")
            elif e.user == "Ozuzanna":
                c.send("ponie")
            elif e.user == "Jr Mime":
                c.send("You have been awarded with TyBot's test trophy!")
            elif e.user == "Stinkowing":
                c.send("Stinko wubs ponies <3!")
            else:
                c.send(e.user + ": I love you. <3")
        elif e.text == "~purge":
            api.call({"action":"purge","titles":"Money_making_guide"})
            c.send("Purged [[Money making guide]]")
        elif e.text == "~updatelogs" and (e.user == "Hairr" or e.user == "TyA") and self.logger_on:
            utc = datetime.utcnow()
            log_page = "Project:Chat/Logs/" + str(utc.day) + " " + utc.strftime("%B") + " " + str(utc.year)
            self.update_logs(user=e.user)
            self.last_updated = time.time()
            c.send("Logs have successfully been updated (located here: [[" + log_page + "]])!")
 
        elif e.text == "~updated" and self.logger_on:
            c.send("The logs have last been updated " + str((time.time() - self.last_updated)) + " seconds ago.")
 
        elif e.text == "~logger_on" and (e.user == "TyA" or e.user == "Hairr") and not self.logger_on:
            c.send("Logger on.  WARNING: If RSChatBot is still in here, to prevent conflicting logging use the command " +
                        "~logger_off (and/or ~dumpbuffer).  My logging system should only be used as backup.")
            self.logger_on = True
            self.last_updated = time.time()
 
        elif e.text == "~logger_off" and (e.user == "TyA" or e.user == "Hairr") and self.logger_on:
            self.logger_on = False
            c.send("Updating logs...")
            self.update_logs()
            c.send("Logger is now off")
 
        elif e.text == "~dumpbuffer" and (e.user == "TyA" or e.user == "Hairr") and self.logger_on:
            # this command should only be used for major things (the buffer is huge from A LOT of spam,
            # logs were just updated so won't cause harm, would cause problem with abusefilter) -- Hairr
            f = open('logs.txt','w')
            c.send("Dumping buffer...")
            f.write("")
            f.close()
            c.send("Buffer from the past hour has been dumped.")
 
        if datetime.utcnow().hour == 0 and self.new_day == False and self.logger_on:
            self.update_logs()
            self.last_updated = time.time()
            self.new_day = True
        elif (time.time()-self.last_updated) >= 3600 and self.logger_on: # causes problems right now, has to be manually updated
            self.update_logs()
            self.last_updated = time.time()
        elif datetime.utcnow().hour == 1 and self.new_day == True and self.logger_on:
            self.new_day = False #resetting
 
    def on_kick(self, c, e):
        if (self.logger_on):
            f = open('logs.txt','a')
            f.write(self.format_message(user=e.user[0].encode('ascii','ignore'),
                                       mod=e.user[1].encode('ascii','ignore'),event='kick'))
            f.close()
 
    def on_ban(self, c, e):
        if (self.logger_on):
            f = open('logs.txt','a')
            f.write(self.format_message(user=e.user[0].encode('ascii','ignore'),
                                        mod=e.user[1].encode('ascii','ignore'),time=e.time,event='ban'))
            f.close()
 
    def update_logs(self, **kwargs):
        f = open('logs.txt','r')
        a = f.read()
        f.close()
        w = open('logs.txt','w')
        w.write("")
        w.close()
        utc = datetime.utcnow()
        global api
        logger_page = api.page("Project:Chat/Logs/%02s %s %s" % (str(utc.day), utc.strftime("%B"), str(utc.year)))
        if kwargs.has_key("user"):
            summary = "Updating chat logs (requested by [[User:" + kwargs["user"] + "|" + kwargs["user"] + "]])"
        else:
            summary = "Updating chat logs"
        try:
            text = logger_page.getcontent().strip("\n</pre>\n[[Category:Wikia Chat logs|%s %02s %02s]]" % (str(utc.year), str(utc.month), str(utc.day)))
        except KeyError:
            text = ''
        if text:
            new_text = text + "\n" + a.replace("<","&lt;").replace(">","&gt;") + "\n</pre>\n[[Category:Wikia Chat logs|%s %02s %02s]]" % (str(utc.year), str(utc.month), str(utc.day))
            logger_page.edit(new_text, summary=summary, bot=True)
        else:
            new_text = '<pre class="ChatLog">\n' + a.replace("<","&lt;").replace(">","&gt;") + "\n</pre>\n[[Category:Wikia Chat logs|%s %02s %02s]] " % (str(utc.year),str(utc.month),str(utc.day))
            logger_page.edit(new_text, summary=summary, bot=True)
 
    def format_message(self, **kwargs):
        time = "%02d:%02d" % (datetime.utcnow().hour, datetime.utcnow().minute)
        if kwargs["event"] == "join":
            return time + " -!- " + kwargs["user"] + " has joined Special:Chat.\n"
        elif kwargs["event"] == "leave":
            return time + " -!- " + kwargs["user"] + " has left Special:Chat.\n"
        elif kwargs["event"] == "message":
            return time + " <" + kwargs["user"] + "> " + kwargs["text"] + "\n"
        elif kwargs["event"] == "kick":
            return time + " -!- " + kwargs["user"] + " was kicked from Special:Chat by " + kwargs["mod"] + "\n"
        elif kwargs["event"] == "ban":
            return time + " -!- " + kwargs["user"] + " was banned from Special:Chat for " + str(kwargs["time"]) + " seconds by " + kwargs["mod"] + ".\n"
try:
  allowed_hosts = yaml.load(open("allowed-hosts.yaml", "r"))["hosts"]
except IOError:
  print >>sys.stderr, "Warning: allowed-hosts.yaml not found or not readable; using the default list ['wikia/vstf/TyA', 'wikia/A-proofreader']"
  allowed_hosts = ['wikia/vstf/TyA', 'wikia/Hairr']
 
def load_cur_price(page):
    page = page.split(":", 1)[1]
 
    try:
        itemid = itemsdict[page]
    except:
        print "Item not found!"
        return False
 
    while True:
        try:
            res = opener.request("GET", ge_url.format(itemid))
            break
        except IOError:
            print >>sys.stderr, "[load_cur_price] - Connection error. Waiting 30 seconds."
            time.sleep(30)
        except:
            print >>sys.stderr, "[load_cur_price] - ERROR. Waiting 30 seconds."
            time.sleep(30)
 
    try:
        prices = json.loads(res.data)
    except ValueError:
        print >>sys.stderr, "[load_cur_price] - Couldn't load JSON"
        return False
 
    cur = max(prices["daily"].keys())
 
    data = [prices["daily"][cur], cur[0:-3]] # [0] = price [1] = unixtimestamp
 
    return data
 
def format_num(x):
    if type(x) not in [type(0), type(0L)]:
        raise TypeError("Parameter must be an integer.")
    if x < 0:
        return "-" + format_num(-x)
    result = ""
    while x >= 1000:
        x, r = divmod(x, 1000)
        result = ",%03d%s" % (r, result)
    return "%d%s" % (x, result)
 
def stale(page):
    q = {"action":"query", "prop":"revisions", "titles":page, "rvprop":"timestamp"}
    query = api.call(q)["query"]["pages"]
    lastrev = query[query.keys()[0]]["revisions"][0]["timestamp"]
    lastrev = api.parse_date(lastrev)
    threshold = timedelta(hours=6)
    try:
        age = time.mktime(datetime.utcnow().timetuple()) - time.mktime(lastrev.timetuple())
    except Exception as e:
        return e#True
    return age > threshold.total_seconds()
 
def update(page):
    print page
    page = "Exchange:"+page
    wikipage = api.page(page)
    if wikipage.exists() is False:
        raise MemoryError("Page does not exist")
    text = wikipage.getcontent()
    newtext = text.split("\n")
    for line in newtext:
        itemid = re.search("\|ItemId=(\d+)", line, re.I)
        price_ = re.search("\|Price=(\S+)$", line, re.I)
        date_ = re.search("\|Date=(.*)$", line, re.I)
        if itemid is not None:
            item_id = itemid.group(1)
        elif price_ is not None:
            old_price = price_.group(1)
            re.sub(",", "", old_price)
            if re.search("^[0-9,]+$", old_price) is None:
                raise TypeError("Invalid characters in the template")
        elif date_ is not None:
            date = date_.group(1)
    #if not stale(page):
    #    print "Page was recently updated."
    #    return False
    newtext = "\n".join(newtext)
    data = load_cur_price(page) # [0] = price, [1]  = unix timestamp
 
    if data == False:
        return False
 
    if re.search("\|Price=", newtext, re.I) is not None:
        newtext = re.sub("Price=\S+", "Price="+format_num(data[0]), newtext, 1)
    if re.search("\|Last=", newtext, re.I) is not None:
        newtext = re.sub("Last=\S+", "Last="+str(old_price), newtext, 1)
    if re.search("\|Date=", newtext, re.I) is not None:
        newtext = re.sub("Date=.*\n", "Date=~~~~~\n", newtext, 1)
    if re.search("\|LastDate=", newtext, re.I) is not None:
 
        newtext = re.sub("LastDate=.*\n", "LastDate="+str(date)+"\n", newtext, 1)
    print format_num(data[0])
 
    result = False
    failures = 0
 
    while result == False and failures <= 2:
        try:
            result = update_data(page,data)
 
        except MemoryError:
            break
        except:
            time.sleep(5)
            failures += 1
 
    result = False
    if failures == 3:
        return False
 
    failures = 0
 
    while result == False and failures <= 2:
        try:
           result = wikipage.edit(content=newtext, summary="Updating price", bot=True)
        except:
            time.sleep(5)
            failures += 1
 
    if failures == 3:
        return False
    else:
        return True
 
def update_data(page, data):
    global volumes
    item = page[9:]
    page = page + "/Data"
 
    try:
        volume = volumes[0][item]
        has_volume = True
    except:
        has_volume = False
 
    wikipage = api.page(page)
    if wikipage.exists() is False:
        raise MemoryError("Page does not exist")
 
    text = wikipage.getcontent()
    text = text.replace("\n}}",",\n")
 
    if has_volume:
        text = text + data[1] + ":" + str(data[0]) + ":" + str(volume) + "\n}}"
    else:
        text = text + data[1] + ":" + str(data[0]) + "\n}}"
 
    #print text
 
    return wikipage.edit(content=text, summary="Updating price data", bot=True)
 
def start_updates(*args):
    global is_running
    global pagesUpdated
    global volumes
    load_items()
    api.login(wiki_username, wiki_password)
    api.setedittoken()
    if args:
        fixed_items = {key: value for i, (key, value) in enumerate(itemsdict.items()) if i >= int(args[0])}
    else:
        fixed_items = dict(itemsdict)
    errorlist = []
    counter = 0
    volumes = getVols()
    with threadlock:
        for page in fixed_items:
            try:
               print  update(page)
            except MemoryError:
                print "Page does not exist"
 
            time.sleep(4)
            pagesUpdated += 1
 
            if on == False:
                break
 
    api.call({"action":"purge", "titles":"Money_making_guide"})
    server.privmsg(channel, "GE updates complete!")
    is_running = False
    pagesUpdated = 0
 
def check_for_updates():
    global is_running
    global checking
    global on
    global runs 
    api.call({"action":"purge","titles":"Template:GEP"})
    time.sleep(20)
    old_price = int(api.call({"action":"expandtemplates", "text":"{{GEP|Abyssal whip}}"})["expandtemplates"]["*"])
 
    while on:
        if(is_running == False and checking == True):
            time.sleep(300)
            if(is_running == False):
                new_price = int(load_cur_price("Exchange:Abyssal whip")[0])
 
                if (new_price != old_price):
                    server.privmsg(channel, "GE Update detected! Starting GE updates... ")
                    is_running = True
                    print "GE update " + str(old_price) + " replaced with " + str(new_price)
                    threading.Thread(target=start_updates).start()
                    runs += 1
 
def irc_handle_msg(connection, event):
    global is_running
    global on
    global checking
    global pagesUpdated
    global check_for_updates_status
    isMod = False
    sender_host = event.source().split("@", 1)[1] #IRC hostmask
    message = event.arguments()[0]
 
    if (sender_host.endswith(".bot.rscript.org")) and (ge_update in message) and (is_running == False):
    #I think every version of RuneScript has a hostmask in the form of "nebulae-v<major>-<minor>.bot.rscript.org", e.g. "nebulae-v4-09.bot.rscript.org", so we only need to check if the host ends with .bot.rscript.org. It's better than checking to see if the entire Nick!ident@host line (in lowercase) starts with "runescript!", because 1) it will work with bots like RuneScript[TH] and 2) It will be much harder to fool the bot into thinking you're RuneScript during a netsplit or something.
        server.privmsg(channel, "Starting GE updates... ")
        is_running = True
        threading.Thread(target=start_updates).start()
 
 
    if(not message.startswith("~")): # if it doesn't end with ~, it isn't a command
        return
 
    if(sender_host in allowed_hosts):
        isMod = True
 
    if(isMod):
        if (message == "~update") and (is_running == False):
            server.privmsg(channel, "Starting GE updates... ")
            is_running = True
            threading.Thread(target=start_updates).start()
 
        elif (message == ("~update")) and (is_running == True):
            server.privmsg(channel, "GE Updater is already running.")
 
        elif (message.startswith("~finish ")):
            server.privmsg(channel, "Finishing GE updates... ")
            is_running = True
            threading.Thread(target=start_updates, args=(message.split(" ", 1)[1],))
 
        elif (message.startswith("~allow ")):
            if (message.split(" ", 1)[1] in allowed_hosts): return
            allowed_hosts.append(message.split(" ", 1)[1])
            yaml.dump({"hosts": allowed_hosts}, open("allowed-hosts.yaml", "w"))
            server.privmsg(channel, message.split(" ", 1)[1] + " is now allowed to use ~update.")
 
        elif (message == "~enablecheck"):
            checking = True
            server.privmsg(channel, "Enabling GE Update Checker")
 
        elif (message == "~disablecheck"):
            checking = False
            server.privmsg(channel, "Disabling GE Update Checker")
        elif (message.startswith("~reload")):
            try:
                load_items()
                server.privmsg(channel, "Successfully updated itemsdict from wiki page :3")
            except:
                server.privmsg(channel, "Updating of itemsdict failed :(")
 
        elif (message == "~restart_chat") and (sender_host in allowed_hosts):
            scbot = ChatBot(wiki_username, wiki_password, "http://runescape.wikia.com")
            scbot.start()
            server.privmsg(channel, "Restarted chat thread; I should be in chat now")
 
    if (message.startswith("~test")):
        server.privmsg(channel, "Hai <3")
    elif (message.startswith("~join")) and (sender_host in ["wikia/TyA", "countervandalism/staff/Sactage"]):
        server.join(message.split(" ", 1)[1]);    
 
 
    elif (message == "~allowed"):
        server.privmsg(channel, "Users allowed to use privileged commands: " + ", ".join(allowed_hosts))
 
    elif (message.startswith("~nick")) and ("wikia/vstf/TyA" in sender_host):
        server.nick(message.split(" ", 1)[1])
 
    elif (message.startswith("~report ")):
        reportee = message.split(" ", 1)[1]
        reportee = reportee[0].upper() + reportee[1:]
        server.privmsg(channel, "Generating an edit report for " + reportee + " at @[[User:TyBot/editreports/" + reportee + "]]" )
        proc = subprocess.Popen("php tybot.editreports.php '" + reportee + "'" , shell=True, stdout=subprocess.PIPE)
 
    elif (message == "~upsrc"):
        proc = subprocess.Popen("php tybot.updateScript.php -f gemw.py -l TyBot/GEMWbot/gemw.py -e python", shell=True, stdout=subprocess.PIPE)
        server.privmsg(channel, "Updated gemw.py at [[w:c:ty:TyBot/GEMWbot/gemw.py]]!")
 
    elif (message == "~help"):
        server.privmsg(channel, "~help - displays this message. ~test displays 'Hai <3'. ~update runs the GE updater. ~allow allows people to run the GE updater. ~allowed displays who can use the GE updater. ~status reports on the GE updater's status. ~report user makes an edit report for user.")
        server.privmsg(channel, "~upsrc updates gemw.py, found at [[w:c:ty:TyBot/GEMWbot/gemw.py]]")
 
    elif (message == "~status"):
        if (is_running == True):
            server.privmsg(channel, "The GE Updater is running, on item %d / %d" % (pagesUpdated, amountOfPages))
        else:
            server.privmsg(channel, "The GE Updater is not running!")
 
    elif (message == "~die" and ("wikia/vstf/TyA" in sender_host)):
        on = False
        server.disconnect()
        exit()
 
 
def irc_handle_getcloak(connection, event):
    server.join(channel)
 
def connect_to_irc():
    server.connect(network, port, nick, ircname=name)
    server.privmsg("NickServ", "IDENTIFY TyBot " + irc_password)
    irc.process_forever()
 
def load_items():
    global itemsdict
    itemsdict = opener.request("GET", "http://ty.wikia.com/wiki/TyBot/GEMWbot/gemwdata.py?action=raw")
    itemsdict = (itemsdict.data).replace('<source lang="python">\n', "")
    fh = open("gemwdata.py", "w")
    fh.write(itemsdict)
    fh.close()
    reload(gemwdata)
    from gemwdata import itemsdict
    itemsdict = collections.OrderedDict(itemsdict)
 
# Start config
on = True
ge_update = "The Grand Exchange has been updated. RuneScript last detected an update"
ge_url = "http://services.runescape.com/m=itemdb_rs/api/graph/{}.json"
opener = urllib3.PoolManager()
threadlock = threading.Lock()
 
 
wiki_username = "TyBot"
wiki_password = getpass.getpass("Wiki password: ")
irc_password = getpass.getpass("IRC password: ")
api = WikiBot("http://runescape.wikia.com/api.php")
api.login(wiki_username, wiki_password)
api.setedittoken()
scbot = ChatBot(wiki_username, wiki_password, "http://runescape.wikia.com")
 
checking = False
 
network = "irc.freenode.net"
port = 6667
channel = "#rswiki"
nick = "tybot"
name = "GEBot"
irclib.DEBUG = True
irc = irclib.IRC()
irc.add_global_handler("pubmsg", irc_handle_msg)
irc.add_global_handler("396", irc_handle_getcloak)
server = irc.server()
 
itemsdict = {}
is_running = False
volumes = []
load_items()
amountOfPages = len(itemsdict)
pagesUpdated = 0
runs = 0
 
threading.Thread(target=check_for_updates).start()
 
print "Connecting to IRC"
threading.Thread(target=connect_to_irc).start()
print "Connecting to Chat"
 
#scbot.start()
#start_updates()

Also on Fandom

Random Wiki