new version of aum backend

This commit is contained in:
Romain Bignon 2011-09-20 21:01:27 +02:00
commit d714250cc8
21 changed files with 1408 additions and 2000 deletions

View file

@ -20,34 +20,40 @@
from __future__ import with_statement
import email
import time
import re
import datetime
from dateutil import tz
from dateutil.parser import parse as _parse_dt
from weboob.capabilities.base import NotLoaded
from weboob.capabilities.chat import ICapChat
from weboob.capabilities.messages import ICapMessages, ICapMessagesPost, Message, Thread
from weboob.capabilities.dating import ICapDating, OptimizationNotFound
from weboob.capabilities.contact import ICapContact, Contact, ContactPhoto, ProfileNode, Query, QueryError
from weboob.capabilities.contact import ICapContact, ContactPhoto, Query, QueryError
from weboob.capabilities.account import ICapAccount, StatusField
from weboob.tools.backend import BaseBackend, BackendConfig
from weboob.tools.browser import BrowserUnavailable
from weboob.tools.value import Value, ValuesDict, ValueBool, ValueBackendPassword
from weboob.tools.log import getLogger
from weboob.tools.misc import html2text, local2utc
from .contact import Contact
from .captcha import CaptchaError
from .antispam import AntiSpam
from .browser import AuMBrowser
from .exceptions import AdopteWait
from .optim.profiles_walker import ProfilesWalker
from .optim.visibility import Visibility
from .optim.priority_connection import PriorityConnection
from .optim.queries_queue import QueriesQueue
__all__ = ['AuMBackend']
def parse_dt(s):
d = _parse_dt(s)
return local2utc(d)
class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapChat, ICapContact, ICapAccount):
NAME = 'aum'
MAINTAINER = 'Romain Bignon'
@ -60,7 +66,6 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
ValueBool('antispam', label='Enable anti-spam', default=False),
ValueBool('baskets', label='Get baskets with new messages', default=True))
STORAGE = {'profiles_walker': {'viewed': []},
'priority_connection': {'config': {}, 'fakes': {}},
'queries_queue': {'queue': []},
'sluts': {},
}
@ -89,7 +94,6 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
def init_optimizations(self):
self.add_optimization('PROFILE_WALKER', ProfilesWalker(self.weboob.scheduler, self.storage, self.browser))
self.add_optimization('VISIBILITY', Visibility(self.weboob.scheduler, self.browser))
self.add_optimization('PRIORITY_CONNECTION', PriorityConnection(self.weboob.scheduler, self.storage, self.browser))
self.add_optimization('QUERIES_QUEUE', QueriesQueue(self.weboob.scheduler, self.storage, self.browser))
# ---- ICapMessages methods ---------------------
@ -99,25 +103,25 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
def iter_threads(self):
with self.browser:
contacts = self.browser.get_threads_list()
threads = self.browser.get_threads_list()
for contact in contacts:
if not contact.get_id():
for thread in threads:
if thread['member'].get('isBan', True):
continue
if self.antispam and not self.antispam.check(contact):
self.logger.info('Skipped a spam-thread from %s' % contact.get_name())
self.report_spam(contact.get_id(), contact.get_suppr_id())
if self.antispam and not self.antispam.check_thread(thread):
self.logger.info('Skipped a spam-thread from %s' % thread['pseudo'])
self.report_spam(thread['member']['id'], thread['id'])
continue
thread = Thread(contact.get_id())
thread.flags = Thread.IS_DISCUSSION
thread.title = 'Discussion with %s' % contact.get_name()
yield thread
t = Thread(int(thread['member']['id']))
t.flags = Thread.IS_DISCUSSION
t.title = 'Discussion with %s' % thread['member']['pseudo']
yield t
def get_thread(self, id, profiles=None, contact=None):
def get_thread(self, id, contacts=None):
"""
Get a thread and its messages.
The 'profiles' and 'contact' parameters are only used for internal calls.
The 'contacts' parameters is only used for internal calls.
"""
thread = None
if isinstance(id, Thread):
@ -125,57 +129,60 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
id = thread.id
if not thread:
thread = Thread(id)
thread = Thread(int(id))
thread.flags = Thread.IS_DISCUSSION
full = False
else:
full = True
with self.browser:
mails = self.browser.get_thread_mails(id, full)
mails = self.browser.get_thread_mails(id, 100)
my_name = self.browser.get_my_name()
child = None
msg = None
slut = self._get_slut(id)
if not profiles:
profiles = {}
for mail in mails:
if contacts is None:
contacts = {}
if not thread.title:
thread.title = u'Discussion with %s' % mails['member']['pseudo']
for mail in mails['messages']:
flags = 0
if self.antispam and not self.antispam.check(mail):
self.logger.info('Skipped a spam-mail from %s' % mail.sender)
self.report_spam(thread.id, contact and contact.get_suppr_id())
if self.antispam and not self.antispam.check_mail(mail):
self.logger.info('Skipped a spam-mail from %s' % mails['member']['pseudo'])
self.report_spam(thread.id, int(mail['id']))
break
if mail.date > slut['lastmsg']:
if parse_dt(mail['date']) > slut['lastmsg']:
flags |= Message.IS_UNREAD
if not mail.profile_link in profiles:
if not mail['id_from'] in contacts:
with self.browser:
profiles[mail.profile_link] = self.browser.get_profile(mail.profile_link)
if self.antispam and not self.antispam.check(profiles[mail.profile_link]):
self.logger.info('Skipped a spam-mail-profile from %s' % mail.sender)
self.report_spam(thread.id, contact and contact.get_suppr_id())
contacts[mail['id_from']] = self.get_contact(mail['id_from'])
if self.antispam and not self.antispam.check_profile(contacts[mail['id_from']].aum_profile):
self.logger.info('Skipped a spam-mail-profile from %s' % mails['member']['pseudo'])
self.report_spam(thread.id, mails['id'])
break
mail.signature += u'\n%s' % profiles[mail.profile_link].get_profile_text()
if mail.sender == my_name:
if mail.new:
if int(mail['id_from']) == self.browser.my_id:
if int(mails['remoteStatus']) == 0:
flags |= Message.IS_NOT_ACCUSED
else:
flags |= Message.IS_ACCUSED
if not thread.title:
thread.title = mail.title
self.storage.set('sluts', thread.id, 'status', mails['remoteStatus'])
msg = Message(thread=thread,
id=mail.message_id,
title=mail.title,
sender=mail.sender,
receivers=[mail.name if mail.sender == my_name else my_name], # TODO: me
date=mail.date,
content=mail.content,
signature=mail.signature,
id=int(time.strftime('%Y%m%d%H%M%S', parse_dt(mail['date']).timetuple())),
title=thread.title,
sender=my_name if int(mail['id_from']) == self.browser.my_id else mails['member']['pseudo'],
receivers=[my_name if int(mail['id_from']) != self.browser.my_id else mails['member']['pseudo']],
date=parse_dt(mail['date']),
content=html2text(mail['message'].replace("\r", "<br>")).strip(),
signature=contacts[mail['id_from']].get_text() if mail['id_from'] in contacts else None,
children=[],
flags=flags)
if child:
@ -198,20 +205,20 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
def iter_unread_messages(self, thread=None):
try:
profiles = {}
contacts = {}
with self.browser:
contacts = self.browser.get_threads_list()
for contact in contacts:
if not contact.get_id():
threads = self.browser.get_threads_list()
for thread in threads:
if thread['member'].get('isBan', True):
continue
if self.antispam and not self.antispam.check(contact):
self.logger.info('Skipped a spam-unread-thread from %s' % contact.get_name())
self.report_spam(contact.get_id(), contact.get_suppr_id())
if self.antispam and not self.antispam.check_thread(thread):
self.logger.info('Skipped a spam-unread-thread from %s' % thread['member']['pseudo'])
self.report_spam(thread['member']['id'], thread['id'])
continue
slut = self._get_slut(contact.get_id())
if contact.get_lastmsg_date() > slut['lastmsg']:
thread = self.get_thread(contact.get_id(), profiles, contact)
for m in thread.iter_all_messages():
slut = self._get_slut(thread['member']['id'])
if parse_dt(thread['date']) > slut['lastmsg'] or int(thread['status']) != int(slut['status']):
t = self.get_thread(thread['member']['id'], contacts)
for m in t.iter_all_messages():
if m.flags & m.IS_UNREAD:
yield m
@ -222,31 +229,31 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
# XXX possibly race condition if a slut adds me in her basket
# between the aum.nb_new_baskets() and aum.get_baskets().
with self.browser:
slut = self._get_slut(-self.MAGIC_ID_BASKET)
new_baskets = self.browser.nb_new_baskets()
if new_baskets:
ids = self.browser.get_baskets()
while new_baskets > 0 and len(ids) > new_baskets:
new_baskets -= 1
if ids[new_baskets] == '-1':
if new_baskets >= 0:
baskets = self.browser.get_baskets()
my_name = self.browser.get_my_name()
for basket in baskets:
if basket['isBan'] or parse_dt(basket['date']) <= slut['lastmsg']:
continue
profile = self.browser.get_profile(ids[new_baskets])
if not profile or profile.get_id() == 0:
continue
if self.antispam and not self.antispam.check(profile):
self.logger.info('Skipped a spam-basket from %s' % profile.get_name())
self.report_spam(profile.get_id())
contact = self.get_contact(basket['id'])
if self.antispam and not self.antispam.check_profile(contact.aum_profile):
self.logger.info('Skipped a spam-basket from %s' % contact.name)
self.report_spam(basket['id'])
continue
thread = Thread(profile.get_id())
thread.title = 'Basket of %s' % profile.get_name()
thread = Thread(int(basket['id']))
thread.title = 'Basket of %s' % contact.name
thread.root = Message(thread=thread,
id=self.MAGIC_ID_BASKET,
title=thread.title,
sender=profile.get_name(),
receivers=[self.browser.get_my_name()],
date=None, # now
sender=contact.name,
receivers=[my_name],
date=parse_dt(basket['date']),
content='You are taken in her basket!',
signature=profile.get_profile_text(),
signature=contact.get_text(),
children=[],
flags=Message.IS_UNREAD)
yield thread.root
@ -256,25 +263,31 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
def set_message_read(self, message):
if message.id == self.MAGIC_ID_BASKET:
# We don't save baskets.
# Save the last baskets checks.
slut = self._get_slut(-self.MAGIC_ID_BASKET)
if slut['lastmsg'] < message.date:
slut['lastmsg'] = message.date
self.storage.set('sluts', -self.MAGIC_ID_BASKET, slut)
self.storage.save()
return
slut = self._get_slut(message.thread.id)
if slut['lastmsg'] < message.date:
slut['lastmsg'] = message.date
#slut['msgstatus'] = contact.get_status()
self.storage.set('sluts', message.thread.id, slut)
self.storage.save()
def _get_slut(self, id):
id = int(id)
sluts = self.storage.get('sluts')
if not sluts or not id in sluts:
slut = {'lastmsg': datetime.datetime(1970,1,1),
'msgstatus': ''}
'status': 0}
else:
slut = self.storage.get('sluts', id)
slut['lastmsg'] = slut['lastmsg'].replace(tzinfo=tz.tzutc())
slut['lastmsg'] = slut.get('lastmsg', datetime.datetime(1970,1,1)).replace(tzinfo=tz.tzutc())
slut['status'] = int(slut.get('status', 0))
return slut
# ---- ICapMessagesPost methods ---------------------
@ -319,59 +332,29 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
if not profile:
return None
_id = profile.id
if profile.is_online():
s = Contact.STATUS_ONLINE
else:
s = Contact.STATUS_OFFLINE
_id = profile['id']
if isinstance(contact, Contact):
contact.id = _id
contact.name = profile.get_name()
contact.status = s
contact.name = profile['pseudo']
else:
contact = Contact(_id, profile.get_name(), s)
contact = Contact(_id, profile['pseudo'], Contact.STATUS_ONLINE)
contact.url = self.browser.id2url(_id)
contact.status_msg = profile.get_status()
contact.summary = profile.description
for photo in profile.photos:
contact.set_photo(photo['url'].split('/')[-1],
url=photo['url'],
thumbnail_url=photo['url'].replace('image', 'thumb1_'),
hidden=photo['hidden'])
contact.profile = []
stats = ProfileNode('stats', 'Stats', [], flags=ProfileNode.HEAD|ProfileNode.SECTION)
for label, value in profile.get_stats().iteritems():
stats.value.append(ProfileNode(label, label.capitalize(), value))
contact.profile.append(stats)
for section, d in profile.get_table().iteritems():
s = ProfileNode(section, section.capitalize(), [], flags=ProfileNode.SECTION)
for key, value in d.iteritems():
s.value.append(ProfileNode(key, key.capitalize(), value))
contact.profile.append(s)
contact.parse_profile(profile, self.browser.get_consts())
return contact
def iter_contacts(self, status=Contact.STATUS_ALL, ids=None):
with self.browser:
for contact in self.browser.iter_contacts():
s = 0
if contact['cat'] == 1:
if contact['isOnline']:
s = Contact.STATUS_ONLINE
elif contact['cat'] == 3:
s = Contact.STATUS_OFFLINE
elif contact['cat'] == 2:
s = Contact.STATUS_AWAY
else:
self.logger.warning('Unknown AuM contact status: %s' % contact['cat'])
s = Contact.STATUS_OFFLINE
if not status & s or ids and contact['id'] in ids:
if not status & s or (ids and not contact['id'] in ids):
continue
# TODO age in contact['birthday']
c = Contact(contact['id'], contact['pseudo'], s)
c.url = self.browser.id2url(contact['id'])
c.status_msg = u'%s old' % contact['birthday']
@ -490,15 +473,12 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
def get_account_status(self):
with self.browser:
try:
return (
StatusField('myname', 'My name', self.browser.get_my_name()),
StatusField('score', 'Score', self.browser.score()),
StatusField('avcharms', 'Available charms', self.browser.nb_available_charms()),
StatusField('godchilds', 'Number of godchilds', self.browser.nb_godchilds()),
)
except AdopteWait:
return (StatusField('notice', '', u'<h3>You are currently waiting 1am to be able to connect with this account</h3>', StatusField.FIELD_HTML|StatusField.FIELD_TEXT))
return (
StatusField('myname', 'My name', self.browser.get_my_name()),
StatusField('score', 'Score', self.browser.score()),
StatusField('avcharms', 'Available charms', self.browser.nb_available_charms()),
StatusField('godchilds', 'Number of godchilds', self.browser.nb_godchilds()),
)
OBJECTS = {Thread: fill_thread,
Contact: fill_contact,