summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Botosh <rumly111@gmail.com>2015-08-27 16:38:28 +0300
committerJoseph Botosh <rumly111@gmail.com>2015-09-03 13:37:46 +0300
commit7e4ac71c32fc041addde6ac2b3abe3330ac3557c (patch)
tree36eafb6717071c37bdfdb42adc37f85720c2841e
parentb4f19a8e28cf09b7c8a042c8faf068217669bca5 (diff)
downloadmanamarket-7e4ac71c32fc041addde6ac2b3abe3330ac3557c.tar.gz
manamarket-7e4ac71c32fc041addde6ac2b3abe3330ac3557c.tar.bz2
manamarket-7e4ac71c32fc041addde6ac2b3abe3330ac3557c.tar.xz
manamarket-7e4ac71c32fc041addde6ac2b3abe3330ac3557c.zip
add !lastseen and !mail commands
-rw-r--r--config.py.template2
-rwxr-xr-xmain.py28
-rw-r--r--onlineusers.py218
-rw-r--r--utils.py16
4 files changed, 261 insertions, 3 deletions
diff --git a/config.py.template b/config.py.template
index e55b2f6..b7437bb 100644
--- a/config.py.template
+++ b/config.py.template
@@ -6,3 +6,5 @@ relist_time = 604800 # Time in seconds before an item needs to be relisted.
character = 0 #slot character is in, 0 for first, 1 for second, 2 for third
admin = ""
#nosell = [] # Items which can't be sold - just add the itemid to the list.
+sqlite3_dbfile = "data/mm.db"
+online_txt_url = 'http://server.themanaworld.org/online.txt'
diff --git a/main.py b/main.py
index 9625fd9..a8e5c45 100755
--- a/main.py
+++ b/main.py
@@ -44,6 +44,7 @@ from player import *
import tradey
import utils
import eliza
+from onlineusers import SqliteDbManager
chatbot = eliza.eliza()
shop_broadcaster = utils.Broadcast()
@@ -55,6 +56,7 @@ user_tree = tradey.UserTree()
sale_tree = tradey.ItemTree()
ItemLog = utils.ItemLog()
logger = logging.getLogger('ManaLogger')
+db_manager = SqliteDbManager(config.sqlite3_dbfile)
def process_whisper(nick, msg, mapserv):
msg = filter(lambda x: x in utils.allowed_chars, msg)
@@ -76,7 +78,7 @@ def process_whisper(nick, msg, mapserv):
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' ]
+ allowed_commands = ['!money', '!help', '!getback', '!info', '!lastseen', "!mail" ]
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)))
@@ -195,6 +197,10 @@ def process_whisper(nick, msg, mapserv):
mapserv.sendall(whisper(nick, "!info - Displays basic information about your account."))
elif broken_string[1] == '!getback':
mapserv.sendall(whisper(nick, "!getback <uid> - 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 <nick> - Show when <nick> was online the last time."))
+ elif broken_string[1] == '!mail':
+ mapserv.sendall(whisper(nick, "!mail <nick> <message> - Send a message to <nick>."))
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"))
@@ -639,6 +645,21 @@ def process_whisper(nick, msg, mapserv):
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 <nick>"))
+ else:
+ ls_info = db_manager.get_lastseen_info(who)
+ mapserv.sendall(whisper(nick, ls_info))
+ elif broken_string[0] == "!mail":
+ to_, msg_ = utils.parse_mail_cmdargs(msg[6:].strip())
+ if to_ == "" or msg_ == "":
+ mapserv.sendall(whisper(nick, "Usage: !mail <nick> <message> OR !mail \"nick with spaces\" <message>"))
+ else:
+ db_manager.send_mail(nick, to_, msg_)
+ mapserv.sendall(whisper(nick, "Message to \"%s\" sent" % (to_)))
+
else:
response = chatbot.respond(msg)
logger.info("Bot Response: "+response)
@@ -770,8 +791,10 @@ def main():
pb = PacketBuffer()
shop_broadcaster.mapserv = mapserv
- # Map server packet loop
+ db_manager.mapserv = mapserv
+ db_manager.start()
+ # Map server packet loop
print "Entering map packet loop\n";
while True:
data = mapserv.recv(2048)
@@ -1109,6 +1132,7 @@ def main():
# On Disconnect/Exit
logger.info("Server disconnect.")
+ db_manager.stop()
shop_broadcaster.stop()
mapserv.close()
diff --git a/onlineusers.py b/onlineusers.py
new file mode 100644
index 0000000..c59834f
--- /dev/null
+++ b/onlineusers.py
@@ -0,0 +1,218 @@
+
+"""
+Copyright 2015, Joseph Botosh <rumly111@gmail.com>
+
+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 <http://www.gnu.org/licenses/>.
+
+Additionally to the GPL, you are *strongly* encouraged to share any modifications
+you do on these sources.
+"""
+
+import sys
+import logging
+import urllib2
+import string
+import sqlite3
+import datetime
+import threading
+import time
+from net.packet_out import whisper
+import config
+
+class OnlineUsers:
+
+ def __init__(self, online_url='http://server.themanaworld.org/online.txt', update_interval=60):
+ self._active = False
+ self._timer = 0
+ self._thread = threading.Thread(target=self._threadfunc, args=())
+ self._url = online_url
+ self._update_interval = update_interval
+ self.__lock = threading.Lock()
+ self.__online_users = []
+
+ @property
+ def online_users(self):
+ self.__lock.acquire(True)
+ users = self.__online_users[:]
+ self.__lock.release()
+ return users
+
+ def dl_online_list(self):
+ """
+ Download online.txt, parse it, and return a list of online user nicks.
+ If error occurs, return empty list
+ """
+ try:
+ data = urllib2.urlopen(self._url).read()
+ except urllib2.URLError, e:
+ # self.logger.error("urllib error: %s", e.message)
+ print ("urllib error: %s" % e.message)
+ return []
+ start = string.find(data, '------------------------------\n') + 31
+ end = string.rfind(data, '\n\n')
+ s = data[start:end]
+ return map(lambda n: n[:-5].strip() if n.endswith('(GM) ') else n.strip(),
+ s.split('\n'))
+
+ def _threadfunc(self):
+ while self._active:
+ if (time.time() - self._timer) > self._update_interval:
+ users = self.dl_online_list()
+ self.__lock.acquire(True)
+ self.__online_users=users
+ self.__lock.release()
+ self._timer = time.time()
+ else:
+ time.sleep(1.0)
+
+ def start(self):
+ self._active = True
+ self._thread.start()
+
+ def stop(self):
+ if self._active:
+ self._active = False
+ self._thread.join()
+
+
+class SqliteDbManager:
+
+ def __init__(self, dbfile):
+ self._active = False
+ self._timer = 0
+ self._lastseen_thread = threading.Thread(target=self.__lastseen_threadfunc, args=())
+ self._mailbox_thread = threading.Thread(target=self.__mailbox_threadfunc, args=())
+ self._dbfile = dbfile
+ self.mapserv = None
+ self._online_manager = OnlineUsers(config.online_txt_url)
+
+ self.db, self.cur = self._open_sqlite_db(dbfile)
+ self.cur.execute('create table if not exists LastSeen(\
+ NICK text[25] not null unique,\
+ DATE_ date not null)')
+ self.cur.execute('create table if not exists MailBox(\
+ ID integer primary key,\
+ FROM_ text[25] not null,\
+ TO_ text[25] not null,\
+ MESSAGE text[255] not null)')
+ self.cur.execute('create unique index if not exists \
+ FROM_TO_IDX on MailBox(FROM_,TO_)')
+ self.db.commit()
+
+ def __del__(self):
+ try:
+ self.db.close()
+ except Exception:
+ pass
+
+ def _open_sqlite_db(self, dbfile):
+ """
+ Open sqlite db, and return tuple (connection, cursor)
+ """
+ try:
+ db = sqlite3.connect(dbfile)
+ cur = db.cursor()
+ except sqlite3.Error, e:
+ # self.logger.error("sqlite3 error: %s", e.message)
+ print ("sqlite3 error: %s" % e.message)
+ sys.exit(1)
+ return db, cur
+
+ def __update_lastseen_info(self, users, db, cur):
+ now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
+ values = map(lambda u: (u, now), users)
+ cur.executemany('replace into LastSeen(NICK,DATE_) values(?,?)',
+ values)
+ db.commit()
+
+ def get_lastseen_info(self, nick):
+ self.cur.execute('select DATE_ from LastSeen where NICK=?',(nick,))
+ self.db.commit() # NOTE: do I need it?
+ row = self.cur.fetchone()
+ if row:
+ return ('%s was seen %s' % (nick, row[0])).encode('utf-8')
+ else:
+ return '%s was never seen' % nick
+
+ def __lastseen_threadfunc(self):
+ print '__lastseen_threadfunc started'
+ db, cur = self._open_sqlite_db(self._dbfile)
+ while self._active:
+ if (time.time() - self._timer) > 60:
+ users = self._online_manager.online_users
+ self.__update_lastseen_info(users, db, cur)
+ self._timer = time.time()
+ else:
+ time.sleep(1.0)
+ db.close()
+
+ def send_mail(self, from_, to_, message):
+ self.cur.execute('replace into MailBox(FROM_,TO_,MESSAGE) values(?,?,?)',
+ (from_,to_,message))
+ self.db.commit()
+
+ def get_unread_mails(self, nick, db=None, cur=None):
+ if db is None:
+ db = self.db
+ if cur is None:
+ cur = self.cur
+ cur.execute('select FROM_,MESSAGE from MailBox where TO_=?',
+ (nick,))
+ db.commit()
+ mails = cur.fetchall()
+ cur.execute('delete from MailBox where TO_=?',
+ (nick,))
+ db.commit()
+ return mails
+
+ def __mailbox_threadfunc(self):
+ print '__mailbox_threadfunc started'
+ db, cur = self._open_sqlite_db(self._dbfile)
+ while self._active:
+ if (time.time() - self._timer) > 60:
+ users = self._online_manager.online_users
+ for u in users:
+ mail = self.get_unread_mails(u, db, cur)
+ nm = len(mail)
+ if nm > 0:
+ self.mapserv.sendall(whisper(u, "You have %d new mails:" % (nm,)))
+ time.sleep(0.7)
+ for m in mail:
+ msg = ("From %s : %s" % (m[0], m[1])).encode('utf-8')
+ self.mapserv.sendall(whisper(u, msg))
+ time.sleep(0.7)
+ self._timer = time.time()
+ else:
+ time.sleep(1.0)
+ db.close()
+
+ def start(self):
+ self._online_manager.start()
+ self._active = True
+ self._lastseen_thread.start()
+ self._mailbox_thread.start()
+
+ def stop(self):
+ if self._active:
+ self._active = False
+ self._lastseen_thread.join()
+ self._mailbox_thread.join()
+ self._online_manager.stop()
+
+
+if __name__=='__main__':
+ print "You should not run this file. Use main.py"
diff --git a/utils.py b/utils.py
index 52d51b6..aad402f 100644
--- a/utils.py
+++ b/utils.py
@@ -14,7 +14,7 @@ import mutex
import threading
from net.packet_out import *
-allowed_chars = "abcdefghijklmnoprstquvwxyzABCDEFGHIJKLMNOPRSTQUVWXYZ1234567890-_+=!@$%^&*();'<>,.?/~`| "
+allowed_chars = "abcdefghijklmnoprstquvwxyzABCDEFGHIJKLMNOPRSTQUVWXYZ1234567890-_+=!@$%^&*();'\"<>,.?/~`| "
# Process a recieved ip address.
def parse_ip(a):
@@ -76,6 +76,20 @@ class ItemDB:
return item_id
return -10 #Not found
+
+def parse_mail_cmdargs(s):
+ if len(s) < 3:
+ return "", ""
+ if s[0] == '"':
+ end = s.find('"', 1)
+ if end > 0:
+ return s[1:end], s[end+2:]
+ else:
+ return "", ""
+ else:
+ end = s.find(' ')
+ return s[:end], s[end+1:]
+
class ItemLog:
""" Writes all sales to a log file, for later processing."""
def __init__(self):