#!/usr/bin/python """ Copyright 2011, Dipesh Amin Copyright 2011, Stefan Beller This file is part of tradey, a trading bot in The Mana World see www.themanaworld.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Additionally to the GPL, you are *strongly* encouraged to share any modifications you do on these sources. """ import logging import logging.handlers import socket import sys import time import string try: import config except: print "no config file found. please move config.py.template to config.py and edit to your needs!" sys.exit(0); from being import * from net.packet import * from net.protocol import * from net.packet_out import * from player import * import tradey import utils import eliza from onlineusers import SqliteDbManager from ircbot import IRCBot from sdnotify import SystemdNotifier chatbot = eliza.eliza() shop_broadcaster = utils.Broadcast() trader_state = utils.TraderState() ItemDB = utils.ItemDB() player_node = Player('') beingManager = BeingManager() user_tree = tradey.UserTree() sale_tree = tradey.ItemTree() ItemLog = utils.ItemLog() logger = logging.getLogger('ManaLogger') db_manager = SqliteDbManager(config.sqlite3_dbfile) ircbot = IRCBot() sd = SystemdNotifier() # How long to wait between WATCHDOG=1 systemd keepalives, in seconds sd_min_keepalive_rate = 5 def process_whisper(nick, msg, mapserv): msg = filter(lambda x: x in utils.allowed_chars, msg) if len(msg) == 0: return # Infinite chat loop anyone? if nick == "guild": return user = user_tree.get_user(nick) broken_string = msg.split() if len(broken_string) == 0: return if user != -10: if int(user.get("accesslevel")) == -1: # A user who has been blocked for abuse. if int(user.get("used_stalls")) == 0 and int(user.get("money")) == 0: mapserv.sendall(whisper(nick, "You can no longer use the bot. If you feel this is in error, please contact" + config.admin)) return allowed_commands = ['!money', '!help', '!getback', '!info' ] if not broken_string[0] in allowed_commands: mapserv.sendall(whisper(nick, "Your access level has been set to blocked! If you feel this is in error, please contact" + config.admin)) mapserv.sendall(whisper(nick, "Though, you still can do the following: "+str(allowed_commands))) return if msg == "!list": # Sends the list of items for sale. if len(sale_tree.root) != 0: mapserv.sendall(whisper(nick, "The following items are on sale:")) else: mapserv.sendall(whisper(nick, "No items for sale.")) for elem in sale_tree.root: if time.time() - float(elem.get('add_time')) < config.relist_time: # Check if an items time is up. msg = "[selling] [" + elem.get("uid") + "] " + elem.get("amount") + " [@@" + \ elem.get("itemId") + "|" + ItemDB.getItem(int(elem.get("itemId"))).name + "@@] for " + elem.get("price") + "gp each" mapserv.sendall(whisper(nick, msg)) elif broken_string[0] == '!selllist': # Support for 4144's shop (Sell list) data = '\302\202B1' for elem in sale_tree.root: if time.time() - float(elem.get('add_time')) < config.relist_time: data += utils.encode_str(int(elem.get("itemId")), 2) data += utils.encode_str(int(elem.get("price")), 4) data += utils.encode_str(int(elem.get("amount")), 3) mapserv.sendall(whisper(nick, data)) elif broken_string[0] == '!buyitem': # 4144 buy command if len(broken_string) == 4: if broken_string[1].isdigit() and broken_string[2].isdigit() and broken_string[3].isdigit(): # Traditional 4144 shop. item_id = int(broken_string[1]) price = int(broken_string[2]) amount = int(broken_string[3]) for elem in sale_tree.root: if int(elem.get('amount')) >= amount and int(elem.get('price')) == price and int(elem.get('itemId')) == item_id: process_whisper(nick, '!buy ' + str(amount) + " " + elem.get('uid'), mapserv) return mapserv.sendall(whisper(nick, "Item not found. Please check and try again.")) else: mapserv.sendall(whisper(nick, "Syntax incorrect")) elif msg == "!info": # Send information related to a player. if user == -10: mapserv.sendall(whisper(nick, "Your current access level is 0. Request access in [@@https://forums.themanaworld.org/viewtopic.php?f=14&t=14010|ManaMarket's forum thread@@]")) elif int(user.get('accesslevel')) > 0: mapserv.sendall(whisper(nick, "Your current access level is " + user.get('accesslevel') + ".")) items_for_sale = False for elem in sale_tree.root: if elem.get('name') == nick: if time.time() - float(elem.get('add_time')) > config.relist_time: msg = "[expired] [" else: msg = "[selling] [" msg += elem.get("uid") + "] " + elem.get("amount") + " [@@" + elem.get("itemId") + "|" + \ ItemDB.getItem(int(elem.get("itemId"))).name + "@@] for " + elem.get("price") + "gp each" if items_for_sale == False: mapserv.sendall(whisper(nick, "Your have the following items for sale:")) items_for_sale = True mapserv.sendall(whisper(nick, msg)) if items_for_sale == False: mapserv.sendall(whisper(nick, "You have no items for sale.")) money = int(user.get('money')) mapserv.sendall(whisper(nick, "You have " + str(money) + "gp to collect.")) stall_msg = "You have " + str(int(user.get('stalls')) - int(user.get('used_stalls'))) + " free slots." mapserv.sendall(whisper(nick, stall_msg)) elif broken_string[0] == "!help": # Sends help information if len(broken_string) == 1: mapserv.sendall(whisper(nick, "Welcome to ManaMarket!")) mapserv.sendall(whisper(nick, "The basic commands for the bot are: !list, !find or , !buy , !add , !money, !relist , !info, !getback ")) mapserv.sendall(whisper(nick, "For a detailed description of each command, type !help e.g. !help !buy")) mapserv.sendall(whisper(nick, "For example to purchase an item shown in the list as:")) mapserv.sendall(whisper(nick, "[selling] [6] 5 [@@640|Iron Ore@@] for 1000gp each")) mapserv.sendall(whisper(nick, "you would type /whisper ManaMarket !buy 1 6" )) mapserv.sendall(whisper(nick, "This will purchase one of item 6 (Iron Ore).")) if user != -10: if int(user.get('accesslevel')) >= 5: mapserv.sendall(whisper(nick,"---")) mapserv.sendall(whisper(nick, "Ah, you have sellers access level. How lovely!")) # the first words the ticket seller told me when i was in london for the first time. How lovely! mapserv.sendall(whisper(nick, "Use !add to tell me which items I should trade for you:")) mapserv.sendall(whisper(nick, "For example !add 10 1000 Iron Ore would tell me to sell 10 [@@640|Iron Ore@@] for a price of 1000 gp")) mapserv.sendall(whisper(nick, "Later you can whisper me !money to get back your money. In the example given, I'd give you 10*1000 = 10000gp")) mapserv.sendall(whisper(nick, "When you just want to know, which items you have given me or how much money I have for you can whisper me !info")) mapserv.sendall(whisper(nick,"If you want to get back an unsold item, whisper me !getback ")) if int(user.get('accesslevel')) == 20: mapserv.sendall(whisper(nick, "You're my master! How should I serve you?")) mapserv.sendall(whisper(nick, "You also have access to the following commands: !listusers, !setslots , !setaccess , !adduser ")) elif len(broken_string) == 2: if broken_string[1] == '!buy': mapserv.sendall(whisper(nick, "!buy - Request the purchase of an item or items.")) elif broken_string[1] == '!list': mapserv.sendall(whisper(nick, "!list - Displays a list of all items for sale.")) elif broken_string[1] == '!find': mapserv.sendall(whisper(nick, "!find or - Simple search to locate an item.")) elif broken_string[1] == '!add': mapserv.sendall(whisper(nick, "!add - Add an item to the sell list (requires that you have an account).")) elif broken_string[1] == '!money': mapserv.sendall(whisper(nick, "!money - Allows you to collect money for any sales made on your behalf.")) elif broken_string[1] == '!relist': mapserv.sendall(whisper(nick, "!relist - Allows you to relist an item which has expired.")) elif broken_string[1] == '!info': mapserv.sendall(whisper(nick, "!info - Displays basic information about your account.")) elif broken_string[1] == '!getback': mapserv.sendall(whisper(nick, "!getback - Allows you to retrieve an item that has expired or you no longer wish to sell.")) elif broken_string[1] == '!lastseen': mapserv.sendall(whisper(nick, "!lastseen - Show when was online the last time.")) elif broken_string[1] == '!mail': mapserv.sendall(whisper(nick, "!mail - Send a message to .")) elif broken_string[1] == '!irc': mapserv.sendall(whisper(nick, "!irc - Enable/disable IRC mode.")) elif user != -10: if int(user.get('accesslevel')) >= 10 and broken_string[1] == '!listusers': mapserv.sendall(whisper(nick, "!listusers - Lists all users which have a special accesslevel, e.g. they are blocked, seller or admin")) elif int(user.get('accesslevel')) >= 10 and broken_string[1] == '!adduser': mapserv.sendall(whisper(nick, "!adduser - Add a user to the bot, a seller should be added with access level 5.")) elif int(user.get('accesslevel')) == 20 and broken_string[1] == '!setslots': mapserv.sendall(whisper(nick, "!setslots - Sets the number of slots available to a given user.")) elif int(user.get('accesslevel')) == 20 and broken_string[1] == '!setaccess': mapserv.sendall(whisper(nick, "!setaccess - Sets access level for the player: -1 is blocked, 5 is seller and 20 is admin" )) elif msg == "!money": # Trades any money earned through item sales. if user == -10: mapserv.sendall(whisper(nick, "You don't have the correct permissions.")) return amount = int(user.get('money')) if amount == 0: mapserv.sendall(whisper(nick, "You have no money to collect.")) else: if not trader_state.Trading.testandset(): mapserv.sendall(whisper(nick, "I'm currently busy with a trade. Try again shortly")) return trader_state.money = nick player_id = beingManager.findId(nick) if player_id != -10: mapserv.sendall(trade_request(player_id)) trader_state.timer = time.time() else: mapserv.sendall(whisper(nick, "Where are you?!? I can't trade with somebody who isn't here!")) trader_state.reset() elif broken_string[0] == "!find": # Returns a list of items, with the corresponding Item Id - !find or . if len(broken_string) < 2: mapserv.sendall(whisper(nick, "Syntax incorrect.")) return items_found = False item = " ".join(broken_string[1:]) # could be an id or an item name if item.isdigit(): # an id for elem in sale_tree.root: if ((time.time() - float(elem.get('add_time'))) < config.relist_time) \ and int(elem.get("itemId")) == int(item): # Check if an items time is up. msg = "[selling] [" + elem.get("uid") + "] " + elem.get("amount") + " [@@" + elem.get("itemId") + "|" \ + ItemDB.getItem(int(elem.get("itemId"))).name + "@@] for " + elem.get("price") + "gp each" mapserv.sendall(whisper(nick, msg)) items_found = True else: # an item name for elem in sale_tree.root: if ((time.time() - float(elem.get('add_time'))) < config.relist_time) \ and item.lower() in ItemDB.getItem(int(elem.get("itemId"))).name.lower(): # Check if an items time is up. msg = "[selling] [" + elem.get("uid") + "] " + elem.get("amount") + " [@@" + elem.get("itemId") + "|" \ + ItemDB.getItem(int(elem.get("itemId"))).name + "@@] for " + elem.get("price") + "gp each" mapserv.sendall(whisper(nick, msg)) items_found = True if not items_found: mapserv.sendall(whisper(nick, "Item not found.")) elif msg == '!tradestate': # Admin command - return trade state. if user == -10: return if int(user.get("accesslevel")) != 20: mapserv.sendall(whisper(nick, "You don't have the correct permissions.")) return if trader_state.Trading.test(): mapserv.sendall(whisper(nick, "I'm busy with a trade.")) else: mapserv.sendall(whisper(nick, "I'm free.")) elif broken_string[0] == '!identify': if user == -10: mapserv.sendall(whisper(nick, "You don't have the correct permissions.")) return elif len(broken_string) != 2: mapserv.sendall(whisper(nick, "Syntax incorrect.")) return elif int(user.get("accesslevel")) < 10: mapserv.sendall(whisper(nick, "You don't have the correct permissions.")) return if broken_string[1].isdigit(): uid = int(broken_string[1]) item_info = sale_tree.get_uid(uid) if item_info == -10: mapserv.sendall(whisper(nick, "Item not found. Please check the uid number and try again.")) return weight = ItemDB.item_names[int(item_info.get('itemId'))].weight*int(item_info.get("amount")) mapserv.sendall(whisper(nick, "That item/s belongs to: "+item_info.get("name"))) mapserv.sendall(whisper(nick, "The weight used is: "+str(weight)+"/"+str(player_node.MaxWEIGHT))) elif msg == '!listusers': # Admin command - shows a list of all user. if user == -10: return if int(user.get("accesslevel")) < 10: mapserv.sendall(whisper(nick, "You don't have the correct permissions.")) return data = '' total_money = 0 total_slots_reserved = 0 total_slots_used = 0 no_users = 0 for user in user_tree.root: no_users += 1 name = user.get('name') accesslevel = user.get('accesslevel') slots = user.get('stalls') total_slots_reserved += int(slots) used_slots = user.get('used_stalls') total_slots_used += int(used_slots) money = user.get('money') total_money += int(money) data += name+" ("+accesslevel+") "+used_slots+"/"+slots+" "+money+'gp, ' # Format ManaMarket (20) 2/5 100000gp, if len(data) > 400: mapserv.sendall(whisper(nick, data[0:len(data)-2]+".")) data = '' if len(data) > 0: mapserv.sendall(whisper(nick, data[0:len(data)-2]+".")) mapserv.sendall(whisper(nick,"Number of users:"+str(no_users)+ ", Sale slots used: "+ \ str(total_slots_used)+"/"+str(total_slots_reserved)+ ", Total Money: "+str(total_money)+\ ", Char slots used: "+str(len(player_node.inventory))+", Weight Used: "+\ str(player_node.WEIGHT)+"/"+str(player_node.MaxWEIGHT))) elif broken_string[0] == '!setslots': # Change the number of slots a user has - !setslots if user == -10: return if int(user.get("accesslevel")) != 20: mapserv.sendall(whisper(nick, "You don't have the correct permissions.")) return if len(broken_string) < 3: mapserv.sendall(whisper(nick, "Syntax incorrect.")) return if broken_string[1].isdigit(): slot = int(broken_string[1]) name = " ".join(broken_string[2:]) user_info = user_tree.get_user(name) if user_info == -10: mapserv.sendall(whisper(nick, "User not found, check and try again.")) return user_tree.get_user(name).set('stalls', str(slot)) mapserv.sendall(whisper(nick, "Slots changed: "+name+" "+str(slot))) tradey.saveData("User: "+name+", Slots changed: "+str(slot)) user_tree.save() else: mapserv.sendall(whisper(nick, "Syntax incorrect.")) elif broken_string[0] == '!setaccess': # Change someones access level - !setaccess if user == -10: return if int(user.get("accesslevel")) != 20: mapserv.sendall(whisper(nick, "You don't have the correct permissions.")) return if len(broken_string) < 3: mapserv.sendall(whisper(nick, "Syntax incorrect.")) return if (broken_string[1][0] == '-' and broken_string[1][1:].isdigit()) or broken_string[1].isdigit(): accesslevel = int(broken_string[1]) name = " ".join(broken_string[2:]) user_info = user_tree.get_user(name) if user_info == -10: mapserv.sendall(whisper(nick, "User not found, check and try again.")) return if int(user_info.get('accesslevel')) < int(user.get("accesslevel")) and accesslevel <= int(user.get("accesslevel")): user_tree.get_user(name).set('accesslevel', str(accesslevel)) mapserv.sendall(whisper(nick, "Access level changed:"+name+ " ("+str(accesslevel)+").")) user_tree.save() tradey.saveData("User: "+name+", Set Access Level: "+str(accesslevel)) else: mapserv.sendall(whisper(nick, "You don't have the correct permissions.")) return else: mapserv.sendall(whisper(nick, "Syntax incorrect.")) elif broken_string[0] == "!adduser": # A command to give a user access to the bot - !adduser . if user == -10: return if int(user.get("accesslevel")) < 10: mapserv.sendall(whisper(nick, "You don't have the correct permissions.")) return if len(broken_string) < 3: mapserv.sendall(whisper(nick, "Syntax incorrect.")) return if broken_string[1].isdigit() and broken_string[2].isdigit(): if int(broken_string[1]) > user.get("accesslevel"): mapserv.sendall(whisper(nick, "You can't give someone a higher accesslevel than your own.")) return al = int(broken_string[1]) stalls = int(broken_string[2]) player_name = " ".join(broken_string[3:]) pl_user = user_tree.get_user(player_name) if pl_user == -10: user_tree.add_user(player_name, stalls, al) mapserv.sendall(whisper(nick, "User Added with " + str(stalls) + " slots.")) tradey.saveData("User Added: "+player_name+", Slots: "+str(stalls)+", Access Level: "+str(al)) else: pl_user.set("accesslevel", str(al)) pl_user.set("stalls", str(stalls)) mapserv.sendall(whisper(nick, "User Added with " + str(stalls) + " slots.")) tradey.saveData("User Added: "+player_name+", Slots: "+str(stalls)+", Access Level: "+str(al)) else: mapserv.sendall(whisper(nick, "Syntax incorrect.")) elif broken_string[0] == "!add": # Allows a player with the correct permissions to add an item for sale - !add if user == -10: mapserv.sendall(whisper(nick, "You are unable to add items. Request access in [@@https://forums.themanaworld.org/viewtopic.php?f=14&t=14010|ManaMarket's forum thread@@]")) return if len(broken_string) < 3: mapserv.sendall(whisper(nick, "Syntax incorrect.")) return if int(user.get("accesslevel")) < 5: mapserv.sendall(whisper(nick, "You are unable to add items.")) return if int(user.get("used_stalls")) >= int(user.get("stalls")): mapserv.sendall(whisper(nick, "You have no free slots. You may remove an item or wait for something to be sold.")) return if broken_string[1].isdigit() and broken_string[2].isdigit(): amount = int(broken_string[1]) price = int(broken_string[2]) item_name = " ".join(broken_string[3:]) item_id = ItemDB.findId(item_name) weight = ItemDB.item_names[item_id].weight*amount if item_id == -10: mapserv.sendall(whisper(nick, "Item not found, check spelling.")) return elif item_id in config.nosell: mapserv.sendall(whisper(nick, "That item can't be added to ManaMarket, as its too heavy.")) return elif int(weight) + player_node.WEIGHT > player_node.MaxWEIGHT: mapserv.sendall(whisper(nick, "I've not got enough room left to carry those. Please try again later. ")) return elif ItemDB.item_names[item_id].weight > 10 and amount > 150: mapserv.sendall(whisper(nick, "Sorry, as each of those items weighs more than 10g you can only add a maximum quantity of 150.")) return if amount > 1 and ItemDB.getItem(item_id).type != 'equip-ammo' and 'equip' in ItemDB.getItem(item_id).type: mapserv.sendall(whisper(nick, "You can only add one piece of equipment per slot.")) return elif price == 0 or price > 50000000: mapserv.sendall(whisper(nick, "Please use a valid price between 1-50000000gp.")) return elif amount == 0: mapserv.sendall(whisper(nick, "You can't add 0 of an item.")) return item = Item() item.player = nick item.get = 1 # 1 = get, 0 = give item.id = item_id item.amount = amount item.price = price if not trader_state.Trading.testandset(): mapserv.sendall(whisper(nick, "I'm currently busy with a trade. Try again shortly")) return trader_state.item = item player_id = beingManager.findId(nick) if player_id != -10: mapserv.sendall(trade_request(player_id)) trader_state.timer = time.time() else: mapserv.sendall(whisper(nick, "Where are you?!? I can't trade with somebody who isn't here!")) trader_state.reset() else: mapserv.sendall(whisper(nick, "Syntax incorrect.")) elif broken_string[0] == "!buy": # Buy a given quantity of an item - !buy if len(broken_string) != 3: mapserv.sendall(whisper(nick, "Syntax incorrect.")) return if broken_string[1].isdigit() and broken_string[2].isdigit(): amount = int(broken_string[1]) uid = int(broken_string[2]) item_info = sale_tree.get_uid(uid) if item_info == -10: mapserv.sendall(whisper(nick, "Item not found. Please check the uid number and try again.")) return if amount > int(item_info.get("amount")): mapserv.sendall(whisper(nick, "I do not have that many.")) return if item_info.get("name") == nick: mapserv.sendall(whisper(nick, "You can not buy your own items. To get back the item whisper me !getback "+broken_string[2])) return item = Item() item.get = 0 # 1 = get, 0 = give item.player = nick item.id = int(item_info.get("itemId")) item.uid = uid item.amount = amount item.price = int(item_info.get("price")) if not trader_state.Trading.testandset(): mapserv.sendall(whisper(nick, "I'm currently busy with a trade. Try again shortly")) return trader_state.item = item player_id = beingManager.findId(nick) if player_id != -10: mapserv.sendall(trade_request(player_id)) trader_state.timer = time.time() mapserv.sendall(whisper(nick, "That will be " + str(item.price * item.amount) + "gp.")) else: mapserv.sendall(whisper(nick, "Where are you?!? I can't trade with somebody who isn't here!")) trader_state.reset() else: mapserv.sendall(whisper(nick, "Syntax incorrect.")) elif broken_string[0] == "!removeuser": # Remove a user, for whatever reason - !removeuser if user == -10: return if len(broken_string) < 2: mapserv.sendall(whisper(nick, "Syntax incorrect.")) return if int(user.get("accesslevel")) != 20: mapserv.sendall(whisper(nick, "You don't have the correct permissions.")) return player_name = " ".join(broken_string[1:]) check = user_tree.remove_user(player_name) if check == 1: mapserv.sendall(whisper(nick, "User Removed.")) tradey.saveData("User Removed: "+player_name) elif check == -10: mapserv.sendall(whisper(nick, "User removal failed. Please check spelling.")) elif broken_string[0] == "!relist": # Relist an item which has expired - !relist . if user == -10 or len(broken_string) != 2: mapserv.sendall(whisper(nick, "Syntax incorrect.")) return if int(user.get("accesslevel")) < 5: mapserv.sendall(whisper(nick, "You don't have the correct permissions.")) return if broken_string[1].isdigit(): uid = int(broken_string[1]) item_info = sale_tree.get_uid(uid) if item_info == -10: mapserv.sendall(whisper(nick, "Item not found. Please check the uid number and try again.")) return if item_info.get('name') != nick: mapserv.sendall(whisper(nick, "That doesn't belong to you!")) return time_relisted = int(item_info.get('relisted')) if int(item_info.get('relisted')) < 3: sale_tree.get_uid(uid).set('add_time', str(time.time())) sale_tree.get_uid(uid).set('relisted', str(time_relisted + 1)) sale_tree.save() mapserv.sendall(whisper(nick, "The item has been successfully relisted.")) user_tree.get_user(nick).set('last_use', str(time.time())) user_tree.save() else: mapserv.sendall(whisper(nick, "This item can no longer be relisted. Please collect it using !getback "+str(uid)+".")) return else: mapserv.sendall(whisper(nick, "Syntax incorrect.")) elif broken_string[0] == "!getback": # Trade the player back uid, remove from sale_items if trade successful - !getback . if user == -10 or len(broken_string) != 2: mapserv.sendall(whisper(nick, "Syntax incorrect.")) return if int(user.get("accesslevel")) < 5 and int(user.get("accesslevel")) > 0: mapserv.sendall(whisper(nick, "You don't have the correct permissions.")) return if broken_string[1].isdigit(): uid = int(broken_string[1]) item_info = sale_tree.get_uid(uid) if item_info == -10: mapserv.sendall(whisper(nick, "Item not found. Please check the uid number and try again.")) return if item_info.get('name') != nick: mapserv.sendall(whisper(nick, "That doesn't belong to you!")) return item = Item() item.get = 0 item.player = nick item.id = int(item_info.get("itemId")) item.uid = uid item.amount = int(item_info.get("amount")) item.price = 0 if not trader_state.Trading.testandset(): mapserv.sendall(whisper(nick, "I'm currently busy with a trade. Try again shortly")) return trader_state.item = item player_id = beingManager.findId(nick) if player_id != -10: mapserv.sendall(trade_request(player_id)) trader_state.timer = time.time() else: mapserv.sendall(whisper(nick, "Where are you?!? I can't trade with somebody who isn't here!")) trader_state.reset() elif broken_string[0] == "!lastseen": who = msg[10:].strip() if len(who) == 0: mapserv.sendall(whisper(nick, "Usage: !lastseen ")) else: ls_info = db_manager.get_lastseen_info(who) mapserv.sendall(whisper(nick, ls_info)) elif broken_string[0] == "!mail": if user == -10: mapserv.sendall(whisper(nick, "Your current access level is 0. Request access in [@@https://forums.themanaworld.org/viewtopic.php?f=14&t=14010|ManaMarket's forum thread@@]")) return to_, msg_ = utils.parse_mail_cmdargs(msg[6:].strip()) if to_ == "" or msg_ == "": mapserv.sendall(whisper(nick, "Usage: !mail OR !mail \"nick with spaces\" ")) else: db_manager.send_mail(nick, to_, msg_) mapserv.sendall(whisper(nick, "Message to \"%s\" sent" % (to_))) elif broken_string[0] == "!irc": if len(broken_string) < 2: mapserv.sendall(whisper(nick, "Incorrect syntax.")) return if broken_string[1] == "on": if user == -10: user_tree.add_user(nick, 0, 0) tradey.saveData("Stub User Added: "+nick+", Slots: 0, Access Level: 0") user = user_tree.get_user(nick) user.set("irc", "on") mapserv.sendall(whisper(nick, "IRC relay mode is now enabled. Reply to chat on IRC.")) elif broken_string[1] == "off": if user != -10: user.set("irc", "off") if int(user.get("accesslevel")) == 0 and int(user.get("stalls")) == 0 and int(user.get("money")) == 0: user_tree.remove_user(nick) tradey.saveData("Stub User Removed: "+nick) mapserv.sendall(whisper(nick, "IRC relay mode is now disabled.")) elif user != -10 and user.get("irc") == "on": if not ircbot.isAFK(msg): # if not an AFK message ircbot.send(nick, msg) db_manager.forEachOnline(broadcast_if_irc_on, nick, "TMW.%s: %s" % (nick, msg)) else: response = chatbot.respond(msg) logger.info("Bot Response: "+response) mapserv.sendall(whisper(nick, response)) #mapserv.sendall(whisper(nick, "Command not recognised, please whisper me !help for a full list of commands.")) def broadcast_from_irc(nick, msg): db_manager.forEachOnline(broadcast_if_irc_on, "IRC", u"IRC.%s: %s" % (nick, msg)) def broadcast_if_irc_on(pl, sender_nick, msg): if sender_nick == pl: return pl_user = user_tree.get_user(pl) if pl_user != -10 and pl_user.get("irc") == "on": mapserv.sendall(whisper(pl, msg.encode("utf-8", "ignore"))) def main(): # Use rotating log files. log_handler = logging.handlers.RotatingFileHandler('data/logs/activity.log', maxBytes=1048576*3, backupCount=5) logger.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S') log_handler.setFormatter(formatter) logger.addHandler(log_handler) logger.info("Bot Started.") account = config.account password = config.password character = config.character login = socket.socket() login.connect((config.server, config.port)) logger.info("Login connected") login_packet = PacketOut(0x0064) login_packet.write_int32(6) # <= CLIENT VERSION login_packet.write_string(account, 24) login_packet.write_string(password, 24) login_packet.write_int8(0x03); # <= FLAGS login.sendall(str(login_packet)) pb = PacketBuffer() id1 = accid = id2 = 0 charip = "" charport = 0 # Login server packet loop. while True: data = login.recv(1500) if not data: break pb.feed(data) for packet in pb: if packet.is_type(SMSG_LOGIN_DATA): # login succeeded packet.skip(2) id1 = packet.read_int32() accid = packet.read_int32() id2 = packet.read_int32() packet.skip(30) player_node.sex = packet.read_int8() charip = utils.parse_ip(packet.read_int32()) charport = packet.read_int16() login.close() break if charip: break assert charport if charip == "127.0.0.1" and config.server != "127.0.0.1": charip = config.server char = socket.socket() char.connect((charip, charport)) logger.info("Char connected") char_serv_packet = PacketOut(CMSG_CHAR_SERVER_CONNECT) char_serv_packet.write_int32(accid) char_serv_packet.write_int32(id1) char_serv_packet.write_int32(id2) char_serv_packet.write_int16(1) # this should match MIN_CLIENT_VERSION in tmwa/src/char/char.hpp char_serv_packet.write_int8(player_node.sex) char.sendall(str(char_serv_packet)) char.recv(4) pb = PacketBuffer() mapip = "" mapport = 0 # Character Server Packet loop. while True: data = char.recv(1500) if not data: break pb.feed(data) for packet in pb: if packet.is_type(SMSG_CHAR_LOGIN): packet.skip(2) slots = packet.read_int16() packet.skip(18) count = (len(packet.data)-22) / 106 for i in range(count): player_node.id = packet.read_int32() player_node.EXP = packet.read_int32() player_node.MONEY = packet.read_int32() packet.skip(62) player_node.name = packet.read_string(24) packet.skip(6) slot = packet.read_int8() packet.skip(1) logger.info("Character information recieved:") logger.info("Name: %s, Id: %s, EXP: %s, MONEY: %s", \ player_node.name, player_node.id, player_node.EXP, player_node.MONEY) if slot == character: break char_select_packet = PacketOut(CMSG_CHAR_SELECT) char_select_packet.write_int8(character) char.sendall(str(char_select_packet)) elif packet.is_type(SMSG_CHAR_MAP_INFO): player_node.id = packet.read_int32() player_node.map = packet.read_string(16) mapip = utils.parse_ip(packet.read_int32()) mapport = packet.read_int16() char.close() break if mapip: break assert mapport if mapip == "127.0.0.1" and charip != "127.0.0.1": mapip = charip beingManager.container[player_node.id] = Being(player_node.id, 42) global mapserv mapserv = socket.socket() mapserv.connect((mapip, mapport)) logger.info("Map connected") mapserv_login_packet = PacketOut(CMSG_MAP_SERVER_CONNECT) mapserv_login_packet.write_int32(accid) mapserv_login_packet.write_int32(player_node.id) mapserv_login_packet.write_int32(id1) mapserv_login_packet.write_int32(id2) mapserv_login_packet.write_int8(player_node.sex) mapserv.sendall(str(mapserv_login_packet)) mapserv.recv(4) pb = PacketBuffer() shop_broadcaster.mapserv = mapserv db_manager.mapserv = mapserv db_manager.start() ircbot.broadcastFunc = broadcast_from_irc ircbot.start() # Functionality for systemd watchdog keepalives last_notify = None def notify_systemd(): global last_sd_notify sd.notify("WATCHDOG=1") last_sd_notify = time.time() return True notify_systemd() # Map server packet loop print "Entering map packet loop\n"; while True: data = mapserv.recv(2048) if not data: break pb.feed(data) # If it's been more than five seconds since we last notified systemd that we're still alive, do so now. if time.time() - last_notify > sd_min_keepalive_rate: notify_systemd() # For unfinished trades - one way to distrupt service would be leaving a trade active. if trader_state.Trading.test(): if time.time() - trader_state.timer > 2*60: logger.info("Trade Cancelled - Timeout.") trader_state.timer = time.time() mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST))) for packet in pb: if packet.is_type(SMSG_MAP_LOGIN_SUCCESS): # connected logger.info("Map login success.") packet.skip(4) coord_data = packet.read_coord_dir() player_node.x = coord_data[0] player_node.y = coord_data[1] player_node.direction = coord_data[2] logger.info("Starting Postion: %s %s %s", player_node.map, player_node.x, player_node.y) mapserv.sendall(str(PacketOut(CMSG_MAP_LOADED))) # map loaded # A Thread to send a shop broadcast: also keeps the network active to prevent timeouts. shop_broadcaster.start() elif packet.is_type(SMSG_PVP_SET): packet.skip(12) elif packet.is_type(SMSG_PVP_MAP_MODE): packet.skip(2) elif packet.is_type(SMSG_QUEST_SET_VAR): packet.skip(6) elif packet.is_type(SMSG_QUEST_PLAYER_VARS): nb = (packet.read_int16() - 4) / 6 for loop in range(nb): packet.skip(6) elif packet.is_type(SMSG_NPC_COMMAND): packet.skip(14) elif packet.is_type(SMSG_BEING_MOVE3): nb = (packet.read_int16() - 14) / 1 packet.skip(10) for loop in range(nb): packet.skip(1) elif packet.is_type(SMSG_MAP_MASK): packet.skip(8) elif packet.is_type(SMSG_MAP_MUSIC): nb = (packet.read_int16() - 4) / 1 for loop in range(nb): packet.skip(1) elif packet.is_type(SMSG_NPC_CHANGETITLE): nb = (packet.read_int16() - 10) / 1 packet.skip(6) for loop in range(nb): packet.skip(1) elif packet.is_type(SMSG_SCRIPT_MESSAGE): nb = (packet.read_int16() - 5) / 1 packet.skip(1) for loop in range(nb): packet.skip(1) elif packet.is_type(SMSG_PLAYER_CLIENT_COMMAND): nb = (packet.read_int16() - 4) / 1 for loop in range(nb): packet.skip(1) elif packet.is_type(SMSG_MAP_SET_TILES_TYPE): packet.skip(32) elif packet.is_type(SMSG_PLAYER_HP): packet.skip(8) elif packet.is_type(SMSG_PLAYER_HP_FULL): packet.skip(12) elif packet.is_type(SMSG_WHISPER): msg_len = packet.read_int16() - 26 nick = packet.read_string(24) message = packet.read_raw_string(msg_len) # Clean up the logs. if nick != 'AuctionBot': logger.info("Whisper: " + nick + ": " + message) process_whisper(nick, utils.remove_colors(message), mapserv) elif packet.is_type(SMSG_PLAYER_STAT_UPDATE_1): stat_type = packet.read_int16() value = packet.read_int32() if stat_type == 0x0018: logger.info("Weight changed from %s/%s to %s/%s", \ player_node.WEIGHT, player_node.MaxWEIGHT, value, player_node.MaxWEIGHT) player_node.WEIGHT = value elif stat_type == 0x0019: logger.info("Max Weight: %s", value) player_node.MaxWEIGHT = value elif packet.is_type(SMSG_PLAYER_STAT_UPDATE_2): stat_type = packet.read_int16() value = packet.read_int32() if stat_type == 0x0014: logger.info("Money Changed from %s, to %s", player_node.MONEY, value) player_node.MONEY = value elif packet.is_type(SMSG_BEING_MOVE) or packet.is_type(SMSG_BEING_VISIBLE)\ or packet.is_type(SMSG_PLAYER_MOVE) or packet.is_type(SMSG_PLAYER_UPDATE_1)\ or packet.is_type(SMSG_PLAYER_UPDATE_2): being_id = packet.read_int32() packet.skip(8) job = packet.read_int16() if being_id not in beingManager.container: if job == 0 and id >= 110000000 and (packet.is_type(SMSG_BEING_MOVE)\ or packet.is_type(SMSG_BEING_VISIBLE)): continue # Add the being to the BeingManager, and request name. beingManager.container[being_id] = Being(being_id, job) requestName = PacketOut(0x0094) requestName.write_int32(being_id) mapserv.sendall(str(requestName)) elif packet.is_type(SMSG_BEING_NAME_RESPONSE): being_id = packet.read_int32() if being_id in beingManager.container: beingManager.container[being_id].name = packet.read_string(24) elif packet.is_type(SMSG_BEING_REMOVE): being_id = packet.read_int32() if being_id in beingManager.container: del beingManager.container[being_id] elif packet.is_type(SMSG_PLAYER_WARP): player_node.map = packet.read_string(16) player_node.x = packet.read_int16() player_node.y = packet.read_int16() logger.info("Player warped: %s %s %s", player_node.map, player_node.x, player_node.y) mapserv.sendall(str(PacketOut(CMSG_MAP_LOADED))) elif packet.is_type(SMSG_PLAYER_INVENTORY_ADD): item = Item() item.index = packet.read_int16() - inventory_offset item.amount = packet.read_int16() item.itemId = packet.read_int16() packet.skip(14) err = packet.read_int8() if err == 0: if item.index in player_node.inventory: player_node.inventory[item.index].amount += item.amount else: player_node.inventory[item.index] = item logger.info("Picked up: %s, Amount: %s, Index: %s", ItemDB.getItem(item.itemId).name, str(item.amount), str(item.index)) elif packet.is_type(SMSG_PLAYER_INVENTORY_REMOVE): index = packet.read_int16() - inventory_offset amount = packet.read_int16() logger.info("Remove item: %s, Amount: %s, Index: %s", ItemDB.getItem(player_node.inventory[index].itemId).name, str(amount), str(index)) player_node.remove_item(index, amount) elif packet.is_type(SMSG_PLAYER_INVENTORY): player_node.inventory.clear() # Clear the inventory - incase of new index. packet.skip(2) number = (len(packet.data)-2) / 18 for loop in range(number): item = Item() item.index = packet.read_int16() - inventory_offset item.itemId = packet.read_int16() packet.skip(2) item.amount = packet.read_int16() packet.skip(10) player_node.inventory[item.index] = item elif packet.is_type(SMSG_PLAYER_EQUIPMENT): packet.read_int16() number = (len(packet.data)) / 20 for loop in range(number): item = Item() item.index = packet.read_int16() - inventory_offset item.itemId = packet.read_int16() packet.skip(16) item.amount = 1 player_node.inventory[item.index] = item logger.info("Inventory information received:") for item in player_node.inventory: logger.info("Name: %s, Id: %s, Index: %s, Amount: %s.", \ ItemDB.getItem(player_node.inventory[item].itemId).name, \ player_node.inventory[item].itemId, item, player_node.inventory[item].amount) errorOccured = player_node.check_inventory(user_tree, sale_tree) if errorOccured: logger.info(errorOccured) shop_broadcaster.stop() sys.exit(1) else: logger.info("Inventory Check Passed.") elif packet.is_type(SMSG_TRADE_REQUEST): name = packet.read_string(24) logger.info("Trade request: " + name) mapserv.sendall(trade_respond(False)) elif packet.is_type(SMSG_TRADE_RESPONSE): response = packet.read_int8() time.sleep(0.2) if response == 0: logger.info("Trade response: Too far away.") if trader_state.item: mapserv.sendall(whisper(trader_state.item.player, "You are too far away.")) elif trader_state.money: mapserv.sendall(whisper(trader_state.money, "You are too far away.")) trader_state.reset() elif response == 3: logger.info("Trade response: Trade accepted.") if trader_state.item: if trader_state.item.get == 1: # add mapserv.sendall(str(PacketOut(CMSG_TRADE_ADD_COMPLETE))) elif trader_state.item.get == 0: # buy if player_node.find_inventory_index(trader_state.item.id) != -10: mapserv.sendall(trade_add_item(player_node.find_inventory_index(trader_state.item.id), trader_state.item.amount)) mapserv.sendall(str(PacketOut(CMSG_TRADE_ADD_COMPLETE))) if trader_state.item.price == 0: # getback mapserv.sendall(str(PacketOut(CMSG_TRADE_OK))) trader_state.complete = 1 else: mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST))) logger.info("Trade response: Trade accepted (buy) - the item could not be added.") mapserv.sendall(whisper(trader_state.item.player, "Sorry, a problem has occured.")) elif trader_state.money: # money amount = int(user_tree.get_user(trader_state.money).get('money')) mapserv.sendall(trade_add_item(0-inventory_offset, amount)) mapserv.sendall(str(PacketOut(CMSG_TRADE_ADD_COMPLETE))) mapserv.sendall(str(PacketOut(CMSG_TRADE_OK))) else: logger.info("Trade response: Trade cancelled") trader_state.reset() elif packet.is_type(SMSG_TRADE_ITEM_ADD): amount = packet.read_int32() item_id = packet.read_int16() if trader_state.item and trader_state.money == 0: if trader_state.item.get == 1: # add if amount == trader_state.item.amount and item_id == trader_state.item.id: trader_state.complete = 1 mapserv.sendall(str(PacketOut(CMSG_TRADE_OK))) elif item_id == 0 and amount > 0: mapserv.sendall(whisper(trader_state.item.player, "Why are you adding money?!?!")) mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST))) else: mapserv.sendall(whisper(trader_state.item.player, "Please check the correct item or quantity has been added.")) mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST))) elif trader_state.item.get == 0: # buy if item_id == 0 and amount == trader_state.item.price * trader_state.item.amount: mapserv.sendall(str(PacketOut(CMSG_TRADE_OK))) trader_state.complete = 1 elif item_id == 0 and amount != trader_state.item.price * trader_state.item.amount: trader_state.complete = 0 else: mapserv.sendall(whisper(trader_state.item.player, "Don't give me your itenz.")) mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST))) elif trader_state.money: # money mapserv.sendall(whisper(trader_state.money, "Don't give me your itenz.")) mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST))) logger.info("Trade item add: ItemId:%s Amount:%s", item_id, amount) # Note item_id = 0 is money elif packet.is_type(SMSG_TRADE_ITEM_ADD_RESPONSE): index = packet.read_int16() - inventory_offset amount = packet.read_int16() response = packet.read_int8() if response == 0: logger.info("Trade item add response: Successfully added item.") if trader_state.item: if trader_state.item.get == 0 and index != 0-inventory_offset: # Make sure the correct item is given! # index & amount are Always 0 if player_node.inventory[index].itemId != trader_state.item.id or \ amount != trader_state.item.amount: logger.info("Index: %s" % index) logger.info("P.ItemId: %s" % player_node.inventory[index].itemId) logger.info("T.ItemId: %s" % trader_state.item.id) logger.info("P.Amount: %s" % amount) logger.info("T.Amount: %s" % trader_state.item.amount) #mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST))) # If Trade item add successful - Remove the item from the inventory state. if index != 0: # If it's not money logger.info("Remove item: %s, Amount: %s, Index: %s", ItemDB.getItem(player_node.inventory[index].itemId).name, str(amount),str(index)) player_node.remove_item(index, amount) else: # The money amount isn't actually sent by the server - odd?!?!?. if trader_state.money: logger.info("Trade: Money Added.") trader_state.complete = 1 elif response == 1: logger.info("Trade item add response: Failed - player overweight.") mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST))) if trader_state.item: mapserv.sendall(whisper(trader_state.item.player, "You are carrying too much weight. Unload and try again.")) elif response == 2: if trader_state.item: mapserv.sendall(whisper(trader_state.item.player, "You have no free slots.")) logger.info("Trade item add response: Failed - No free slots.") mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST))) else: logger.info("Trade item add response: Failed - unknown reason.") mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST))) if trader_state.item: mapserv.sendall(whisper(trader_state.item.player, "Sorry, a problem has occured.")) elif packet.is_type(SMSG_TRADE_OK): is_ok = packet.read_int8() # 0 is ok from self, and 1 is ok from other if is_ok == 0: logger.info("Trade OK: Self.") else: if trader_state.complete: mapserv.sendall(str(PacketOut(CMSG_TRADE_OK))) else: mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST))) if trader_state.item: mapserv.sendall(whisper(trader_state.item.player, "Trade Cancelled: Please check the traded items or money.")) logger.info("Trade Ok: Partner.") elif packet.is_type(SMSG_TRADE_CANCEL): trader_state.reset() logger.info("Trade Cancel.") elif packet.is_type(SMSG_TRADE_COMPLETE): commitMessage="" # The sale_tree is only ammended after a complete trade packet. if trader_state.item and trader_state.money == 0: if trader_state.item.get == 1: # !add sale_tree.add_item(trader_state.item.player, trader_state.item.id, trader_state.item.amount, trader_state.item.price) user_tree.get_user(trader_state.item.player).set('used_stalls', \ str(int(user_tree.get_user(trader_state.item.player).get('used_stalls')) + 1)) user_tree.get_user(trader_state.item.player).set('last_use', str(time.time())) commitMessage = "Add" elif trader_state.item.get == 0: # !buy \ !getback seller = sale_tree.get_uid(trader_state.item.uid).get('name') item = sale_tree.get_uid(trader_state.item.uid) current_amount = int(item.get("amount")) sale_tree.get_uid(trader_state.item.uid).set("amount", str(current_amount - trader_state.item.amount)) if int(item.get("amount")) == 0: user_tree.get_user(sale_tree.get_uid(trader_state.item.uid).get('name')).set('used_stalls', \ str(int(user_tree.get_user(sale_tree.get_uid(trader_state.item.uid).get('name')).get('used_stalls'))-1)) sale_tree.remove_item_uid(trader_state.item.uid) current_money = int(user_tree.get_user(seller).get("money")) user_tree.get_user(seller).set("money", str(current_money + trader_state.item.price * trader_state.item.amount)) if trader_state.item.price * trader_state.item.amount != 0: ItemLog.add_item(int(item.get('itemId')), trader_state.item.amount, trader_state.item.price * trader_state.item.amount, item.get('name')) commitMessage = "Buy or Getback" elif trader_state.money and trader_state.item == 0: # !money user_tree.get_user(trader_state.money).set('money', str(0)) commitMessage = "Money" sale_tree.save() user_tree.save() tradey.saveData(commitMessage) trader_state.reset() logger.info("Trade Complete.") errorOccured = player_node.check_inventory(user_tree, sale_tree) if errorOccured: logger.info(errorOccured) shop_broadcaster.stop() sys.exit(1) else: pass # On Disconnect/Exit logger.info("Server disconnect.") db_manager.stop() shop_broadcaster.stop() mapserv.close() if __name__ == '__main__': main()