onlineusers.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. """
  2. Copyright 2015, Joseph Botosh <rumly111@gmail.com>
  3. This file is part of tradey, a trading bot in The Mana World
  4. see www.themanaworld.org
  5. This program is free software; you can redistribute it and/or modify it
  6. under the terms of the GNU General Public License as published by the Free
  7. Software Foundation; either version 2 of the License, or (at your option)
  8. any later version.
  9. This program is distributed in the hope that it will be useful, but WITHOUT
  10. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11. FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12. more details.
  13. You should have received a copy of the GNU General Public License along with
  14. this program. If not, see <http://www.gnu.org/licenses/>.
  15. Additionally to the GPL, you are *strongly* encouraged to share any modifications
  16. you do on these sources.
  17. """
  18. import sys
  19. import logging
  20. import urllib2
  21. import string
  22. import sqlite3
  23. import datetime
  24. import threading
  25. import time
  26. from net.packet_out import whisper
  27. import config
  28. class OnlineUsers:
  29. def __init__(self, online_url='http://server.themanaworld.org/online-old.txt', update_interval=20):
  30. self._active = False
  31. self._timer = 0
  32. self._thread = threading.Thread(target=self._threadfunc, args=())
  33. self._url = online_url
  34. self._update_interval = update_interval
  35. self.__lock = threading.Lock()
  36. self.__online_users = []
  37. @property
  38. def online_users(self):
  39. self.__lock.acquire(True)
  40. users = self.__online_users[:]
  41. self.__lock.release()
  42. return users
  43. def dl_online_list(self):
  44. """
  45. Download online.txt, parse it, and return a list of online user nicks.
  46. If error occurs, return empty list
  47. """
  48. try:
  49. data = urllib2.urlopen(self._url).read()
  50. except urllib2.URLError, e:
  51. # self.logger.error("urllib error: %s", e.message)
  52. print ("urllib error: %s" % e.message)
  53. return []
  54. start = string.find(data, '------------------------------\n') + 31
  55. end = string.rfind(data, '\n\n')
  56. s = data[start:end]
  57. return map(lambda n: n[:-5].strip() if n.endswith('(GM) ') else n.strip(),
  58. s.split('\n'))
  59. def _threadfunc(self):
  60. while self._active:
  61. if (time.time() - self._timer) > self._update_interval:
  62. users = self.dl_online_list()
  63. self.__lock.acquire(True)
  64. self.__online_users=users
  65. self.__lock.release()
  66. self._timer = time.time()
  67. else:
  68. time.sleep(1.0)
  69. def start(self):
  70. self._active = True
  71. self._thread.start()
  72. def stop(self):
  73. if self._active:
  74. self._active = False
  75. self._thread.join()
  76. class SqliteDbManager:
  77. def __init__(self, dbfile):
  78. self._active = False
  79. self._timer = 0
  80. self._lastseen_thread = threading.Thread(target=self.__lastseen_threadfunc, args=())
  81. self._mailbox_thread = threading.Thread(target=self.__mailbox_threadfunc, args=())
  82. self._dbfile = dbfile
  83. self.mapserv = None
  84. self._online_manager = OnlineUsers(config.online_txt_url, config.online_txt_interval)
  85. self.db, self.cur = self._open_sqlite_db(dbfile)
  86. self.cur.execute('create table if not exists LastSeen(\
  87. NICK text[25] not null unique,\
  88. DATE_ date not null)')
  89. self.cur.execute('create table if not exists MailBox(\
  90. ID integer primary key,\
  91. FROM_ text[25] not null,\
  92. TO_ text[25] not null,\
  93. MESSAGE text[255] not null)')
  94. self.cur.execute('create unique index if not exists \
  95. FROM_TO_IDX on MailBox(FROM_,TO_)')
  96. self.db.commit()
  97. def __del__(self):
  98. try:
  99. self.db.close()
  100. except Exception:
  101. pass
  102. def _open_sqlite_db(self, dbfile):
  103. """
  104. Open sqlite db, and return tuple (connection, cursor)
  105. """
  106. try:
  107. db = sqlite3.connect(dbfile)
  108. cur = db.cursor()
  109. except sqlite3.Error, e:
  110. # self.logger.error("sqlite3 error: %s", e.message)
  111. print ("sqlite3 error: %s" % e.message)
  112. sys.exit(1)
  113. return db, cur
  114. def __update_lastseen_info(self, users, db, cur):
  115. now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
  116. values = map(lambda u: (u, now), users)
  117. cur.executemany('replace into LastSeen(NICK,DATE_) values(?,?)',
  118. values)
  119. db.commit()
  120. def get_lastseen_info(self, nick):
  121. try:
  122. self.cur.execute('select DATE_ from LastSeen where NICK=?',(nick,))
  123. self.db.commit() # NOTE: do I need it?
  124. row = self.cur.fetchone()
  125. except sqlite3.Error, e:
  126. print ("sqlite3 error: %s" % e.message)
  127. row = ["although I do not remember when."]
  128. if row:
  129. return ('%s was seen %s' % (nick, row[0])).encode('utf-8')
  130. else:
  131. return '%s was never seen' % nick
  132. def __lastseen_threadfunc(self):
  133. print '__lastseen_threadfunc started'
  134. db, cur = self._open_sqlite_db(self._dbfile)
  135. while self._active:
  136. if (time.time() - self._timer) > 60:
  137. users = self._online_manager.online_users
  138. self.__update_lastseen_info(users, db, cur)
  139. self._timer = time.time()
  140. else:
  141. time.sleep(1.0)
  142. db.close()
  143. def send_mail(self, from_, to_, message):
  144. try:
  145. self.cur.execute('replace into MailBox(FROM_,TO_,MESSAGE) values(?,?,?)',
  146. (from_,to_,message))
  147. self.db.commit()
  148. except sqlite3.Error, e:
  149. print ("sqlite3 error: %s" % e.message)
  150. def get_unread_mails(self, nick, db=None, cur=None):
  151. try:
  152. if db is None:
  153. db = self.db
  154. if cur is None:
  155. cur = self.cur
  156. cur.execute('select FROM_,MESSAGE from MailBox where TO_=?',
  157. (nick,))
  158. db.commit()
  159. mails = cur.fetchall()
  160. cur.execute('delete from MailBox where TO_=?',
  161. (nick,))
  162. db.commit()
  163. except sqlite3.Error, e:
  164. print ("sqlite3 error: %s" % e.message)
  165. mails = []
  166. return mails
  167. def __mailbox_threadfunc(self):
  168. print '__mailbox_threadfunc started'
  169. db, cur = self._open_sqlite_db(self._dbfile)
  170. while self._active:
  171. if (time.time() - self._timer) > 60:
  172. users = self._online_manager.online_users
  173. for u in users:
  174. mail = self.get_unread_mails(u, db, cur)
  175. nm = len(mail)
  176. if nm > 0:
  177. self.mapserv.sendall(whisper(u, "You have %d new mails:" % (nm,)))
  178. time.sleep(0.7)
  179. for m in mail:
  180. msg = ("From %s : %s" % (m[0], m[1])).encode('utf-8')
  181. self.mapserv.sendall(whisper(u, msg))
  182. time.sleep(0.7)
  183. self._timer = time.time()
  184. else:
  185. time.sleep(1.0)
  186. db.close()
  187. def forEachOnline(self, callback, *args):
  188. users = self._online_manager.online_users
  189. for u in users:
  190. callback(u, *args)
  191. def start(self):
  192. self._online_manager.start()
  193. self._active = True
  194. self._lastseen_thread.start()
  195. self._mailbox_thread.start()
  196. def stop(self):
  197. if self._active:
  198. self._active = False
  199. self._lastseen_thread.join()
  200. self._mailbox_thread.join()
  201. self._online_manager.stop()
  202. if __name__=='__main__':
  203. print "You should not run this file. Use main.py"