Beispiel #1
0
    def render(self, variables, content_template,  pretty=False):
        """
        Works like salmon.view.render, but uses apply_styles to modify
        the HTML with the configured CSS before returning it to you.

        If you set the pretty=True then it will prettyprint the results,
        which is a waste of bandwidth, but helps when debugging.

        Remember that content_template is run through the template system,
        and then processed with self.wiki (defaults to markdown).  This
        let's you do template processing and write the HTML contents like
        you would an email.

        You could also attach the content_template as a text version of the
        message for people without HTML.  Simply set the .Body attribute
        of the returned salmon.mail.MailResponse object.
        """
        content = self.wiki(view.render(variables, content_template))
        lvars = variables.copy()
        lvars['content'] = content

        html = view.render(lvars, self.template)
        styled = self.apply_styles(html)

        if pretty:
            return styled.prettify()
        else:
            return str(styled)
Beispiel #2
0
def ENROLL(message, nonce, host=None):
    server_name = server_name_config
    service_address = 'pm-enroll-%s' % (nonce,)

    # check if the email address is know, if it is, croak
    sender = c.place_sender(message)
    if sender == INTERNAL:
        logging.debug(u"INTERNAL ignoring %s@%s from %s" %
                      (service_address, server_name, message['from']))        
        return # ignore    
    if sender != UNKNOWN:
        msg = "Already playing"
        if type(sender) is tuple:
            msg = msg + " " + c.campaign_name(sender[0])
        if silent:
            logging.debug(u"ALREADY ignoring %s@%s from %s - already playing" %
                      (service_address, server_name, message['from']))
            return
        raise SMTPError(550, msg)

    enrollment = c.find_enrollment(nonce)

    if enrollment is None:
        if silent:
            logging.debug(u"INVALID code, ignoring %s@%s from %s" %
                      (service_address, server_name, message['from']))
            return
        raise SMTPError(550, "Enrollment code invalid")

    (cid, short_form) = enrollment

    c.do_enrollment(cid, nonce, message['from'], short_form)

    lang = c.campaign_language(cid)
    campaign_name = c.campaign_name(cid)
    attribution = c.get_attribution(message['from'])

    msg = view.respond(locals(), "%s/enrolled_pc.msg" % (lang,),
                           From="%s@%s" % (service_address, server_name),
                           To=message['from'],
                           Subject=view.render(locals(),
                                                   "%s/enrolled_pc.subj" % (lang,)))

    logging.debug(u"ENROLLED short form %s, enrolled at %s, campaign %s" %
                      (short_form, service_address, str(cid)))
    send_or_queue(msg, cid)

    (gm_address, gm_full, attribution) = c.campaign_gm(cid)
    msg = view.respond(locals(), "%s/enrolled_gm.msg" % (lang,),
                           From="pm-enroll@%s" % (server_name,),
                           To=gm_full,
                           Subject=view.render(locals(),
                                                   "%s/enrolled_gm.subj" % (lang,)))
    send_or_queue(msg, cid)
    return
Beispiel #3
0
def PC_END(message, nonce, host=None):
    # drop to NPC and inform the GM

    service_address = 'pm-unenroll-%s' % (nonce,)
    server_name = server_name_config        
    
    sender = c.place_sender(message)
    if sender == INTERNAL:
        logging.debug(u"INTERNAL ignoring %s@%s from %s" %
                      (service_address, server_name, message['from']))        
        return # ignore    
    if sender == UNKNOWN:
        if silent:
            return
        raise SMTPError(550, "Unknown sender")

    unenrollment = c.find_unenrollment(nonce)

    if unenrollment is None:
        if silent:
            logging.debug(u"INVALID code, ignoring %s@%s from %s" %
                      (service_address, server_name, message['from']))
            return
        raise SMTPError(550, "Unenrollment code invalid")

    (cid, short_form) = unenrollment

    lang = c.campaign_language(cid)
    campaign_name = c.campaign_name(cid)
    attribution = c.get_attribution(message['from'])
    
    c.do_unenrollment(cid, nonce)
    logging.debug(u"UNENROLLED short form %s, unenrolled at %s, campaign %s" %
                      (short_form, service_address, str(cid)))

    msg = view.respond(locals(), "%s/unenrolled_pc.msg" % (lang,),
                           From="%s@%s" % (service_address, server_name),
                           To=message['from'],
                           Subject=view.render(locals(),
                                                   "%s/unenrolled_pc.subj" % (lang,)))
    send_or_queue(msg, cid)

    # inform the GM
    (gm_address, gm_full, attribution) = c.campaign_gm(cid)
    msg = view.respond(locals(), "%s/unenrolled_gm.msg" % (lang,),
                           From="pm-unenrolled@%s" % (server_name,),
                           To=gm_full,
                           Subject=view.render(locals(),
                                                   "%s/unenrolled_gm.subj" % (lang,)))
    send_or_queue(msg, cid)    
    return
Beispiel #4
0
def GAME_END(message, host=None):
    service_address = 'pm-end'
    server_name = server_name_config        
    
    sender = c.place_sender(message)
    if sender == INTERNAL:
        return # ignore    
    if sender == UNKNOWN:
        if silent:
            return
        raise SMTPError(550, "Unknown sender")

    cid = sender[0]
    lang = c.campaign_language(cid)
    campaign_name = c.campaign_name(cid)
    
    if sender[1]:
        # GM, pack and go
        logging.debug(u"END_GAME request from gm, campaign %s" % (str(cid),))

        all_characters = c.all_characters(cid)
        emails = set() # save emails before the purge
        for character in all_characters:
            emails.add(character['controller'])

        report_id  = end_campaign(cid)
        report_url = '%s/%s.zip' % (campaign_reports_url_config, report_id)

        for email in emails:
            msg = view.respond(locals(), "%s/end_game.msg" % (lang,),
                From="%s@%s" % (service_address, server_name,),
                To=email,
                Subject=view.render(locals(), "%s/end_game.subj" % (lang,)))
            send_or_queue(msg, cid)
    else:
        # PC, generate unenrollment address
        short_form = sender[2]
        unenrollment_address = t.start_unenrollment(cid, short_form)
        
        msg = view.respond(locals(), "%s/drop_pc.msg" % (lang,),
                            From="%s@%s" % (service_address, server_name),
                            To=message['from'],
                            Subject=view.render(locals(),
                                                    "%s/drop_pc.subj" % (lang,)))
        msg['Reply-To'] = "pm-unenroll-%s@%s" % (unenrollment_address, server_name)

        logging.debug(u"END_PC short form %s, unenroll at %s, campaign %s" %
                        (short_form, unenrollment_address, str(cid)))
        send_or_queue(msg, cid)
    return
Beispiel #5
0
def POSTING(message, post_name=None, host=None):
    user, address = parseaddr(message['from'])
    user = user or address
    post_url = "posts/%s/%s.html" % (address, post_name)

    index_q = queue.Queue("run/indexed")
    post_keys = sorted(index_q.keys(), reverse=True)
    old_keys = post_keys[50:]
    del post_keys[50:]

    # find the old one and remove it
    posts = []
    for key in post_keys:
        msg = index_q.get(key)
        if msg['x-post-url'] == post_url:
            # this is the old one, take it out
            index_q.remove(key)
        else:
            posts.append(msg)

    # update the index and our posts
    message['X-Post-URL'] = post_url
    index_q.push(message)
    posts.insert(0, message)

    # and generate the index with what we got now
    index = view.render(locals(), "web/index.html")

    f = open("app/data/index.html", "w")
    f.write(index.encode("utf-8"))
    f.close()

    # finally, zap all the old keys
    for old in old_keys: index_q.remove(old)
Beispiel #6
0
    def respond(self, variables, content, **kwd):
        """
        Works like salmon.view.respond letting you craft a
        salmon.mail.MailResponse immediately from the results of
        a salmon.html.HtmlMail.render call.  Simply pass in the
        From, To, and Subject parameters you would normally pass
        in for MailResponse, and it'll craft the HTML mail for
        you and return it ready to deliver.

        A slight convenience in this function is that if the
        Body kw parameter equals the content parameter, then
        it's assumed you want the raw markdown content to be
        sent as the text version, and it will produce a nice
        dual HTML/text email.
        """
        assert content, "You must give a contents template."

        if kwd.get('Body', None) == content:
            kwd['Body'] = view.render(variables, content)

        for key in kwd:
            kwd[key] = kwd[key] % variables
        
        msg = mail.MailResponse(**kwd)
        msg.Html = self.render(variables, content)

        return msg
Beispiel #7
0
def test_spelling():
    message = {}
    original = {}
    for path in glob("app/templates/mail/*.msg"):
        template = "mail/" + os.path.basename(path)
        result = view.render(locals(), template)
        spelling(template, result)
Beispiel #8
0
def NEW_ATTRIBUTION(message, host=None):
    service_address = 'pm-new-attribution'
    server_name = server_name_config
    
    # check the sender is known
    sender = c.place_sender(message)
    if sender == INTERNAL:
        logging.debug(u"INTERNAL ignoring %s@%s from %s" %
                      (service_address, server_name, message['from']))
        return # ignore
    if sender == UNKNOWN:
        if silent:
            logging.debug(u"UNKNOWN ignoring %s@%s from %s" %
                      (service_address, server_name, message['from']))
            return
        raise SMTPError(550, "Unknown sender")
    
    cid = sender[0]
    lang = c.campaign_language(cid)
    campaign_name = c.campaign_name(cid)
    attribution = message['subject']
    c.set_attribution(message['from'], attribution)

    msg = view.respond(locals(), "%s/new_attribution.msg" % (lang,),
                           From="%s@%s" % (service_address, server_name),
                           To=message['from'],
                           Subject=view.render(locals(),
                                               "%s/new_attribution.subj"  % (lang,)))
    logging.debug(u"New attribution: %s attribute to %s" % (message['from'],attribution))
    send_or_queue(msg, cid)
    return
Beispiel #9
0
def test_HtmlMail_apply_styles():
    hs = html.HtmlMail("style.css", "html_test.html")
    page = view.render(locals(), "html_test.html")

    styled = hs.apply_styles(page)

    assert "magenta" in str(styled)
    assert_not_equal(str(styled), str(page))
Beispiel #10
0
def test_HtmlMail_apply_styles():
    hs = html.HtmlMail("style.css", "html_test.html")
    page = view.render(locals(), "html_test.html")

    styled = hs.apply_styles(page)

    assert "magenta" in str(styled)
    assert_not_equal(str(styled), str(page))
Beispiel #11
0
 def load_css(self, css_template, variables):
     """
     If you want to change the CSS, simply call this with the new CSS and variables.
     It will change internal state so that later calls to render or respond use
     the new CSS.
     """
     self.css = view.render(variables, css_template)
     self.engine = clevercss.Engine(self.css)
     self.stylesheet = []
     
     for selector, style in self.engine.evaluate():
         attr = "; ".join("%s: %s" % (k,v) for k,v in style)
         selectors = selector[0].split()
         # root, path, attr
         self.stylesheet.append((selectors[0], selectors[1:], attr))
Beispiel #12
0
def post(post_name, user, host, message):
    user_dir = make_user_dir(user)
    user_id, domain = user.split("@")

    # make sure it's removed first if it existed
    delete(post_name, user)

    posting = open("%s/%s.html" % (user_dir, post_name), "w")
    content = markdown(message.body())

    html = view.render(locals(), "web/post.html")

    posting.write(html.encode('utf-8'))

    post_q = get_user_post_queue(user_dir)
    post_q.push(message)
Beispiel #13
0
def post(post_name, user, host, message):
    user_dir = make_user_dir(user)
    user_id, domain = user.split("@")

    # make sure it's removed first if it existed
    delete(post_name, user)

    posting = open("%s/%s.html" % (user_dir, post_name), "w")
    content = markdown(message.body())

    html = view.render(locals(), "web/post.html")

    posting.write(html.encode('utf-8'))

    post_q = get_user_post_queue(user_dir)
    post_q.push(message)
Beispiel #14
0
def COMMENTING(message, user_id=None, domain=None, post_name=None, host=None):
    address = user_id + '@' + domain
    user_dir = post.get_user_dir(address)

    if post.user_exists(address):
        # stuff it here for now, but we'll just build the file rolling
        comments = queue.Queue("%s/comments" % user_dir)
        comments.push(message)

        contents = markdown(message.body())
        comment_file = "%s/%s-comments.html" % (user_dir, post_name)
        snippet = view.render(locals(), "web/comments.html")
        with open(comment_file, "a") as out:
            out.write(snippet)

    else:
        logging.warning("Attempt to post to user %r but user doesn't exist.", address)
Beispiel #15
0
def GAME_SUMMARY(message, host=None):
    service_address = 'pm-summary'
    server_name = server_name_config        
    
    sender = c.place_sender(message)
    if sender == INTERNAL:
        return # ignore    
    if sender == UNKNOWN:
        if silent:
            return
        raise SMTPError(550, "Unknown sender")

    cid = sender[0]
    lang = c.campaign_language(cid)
    campaign_name = c.campaign_name(cid)
    
    if not sender[1]:
        if silent:
            logging.debug(u"NOT_GM ignoring %s@%s from %s" %
                      (service_address, server_name, message['from']))
            return
        raise SMTPError(550, "Not a GM")

    logging.debug(u"GAME_SUMMARY request from gm, campaign %s" % (str(cid),))
    
    all_characters = c.all_characters(cid)
    emails = set() # save emails before the purge
    for character in all_characters:
        emails.add(character['controller'])

    report_id  = end_campaign(cid, purge=False)
    report_url = '%s/%s.zip' % (campaign_reports_url_config, report_id)

    msg = view.respond(locals(), "%s/game_summary.msg" % (lang,),
                    From="%s@%s" % (service_address, server_name,),
                    To=message['from'],
                    Subject=view.render(locals(), "%s/game_summary.subj" % (lang,)))
    send_or_queue(msg, cid)
    return
Beispiel #16
0
def _new_campaign(message, lang, service_address):
    
    # check if the email address is know, if it is, croak
    sender = c.place_sender(message)
    if sender == INTERNAL:
        return # ignore

    server_name = server_name_config        
    if sender != UNKNOWN:
        msg = "Already playing"
        if type(sender) is tuple:
            msg = msg + " " + c.campaign_name(sender[0])
        if silent:
            logging.debug(u"MESSAGE to %s@%s from %s - already playing" %
                      (service_address, server_name, message['from']))
            return
        raise SMTPError(550, msg)
    
    # use subject for name of campaign
    campaign_name = message['subject']
    
    # create campaign, set language to lang
    cid = c.new_campaign(campaign_name, message['from'], lang)

    # generate reply
    attribution = c.get_attribution(message['from'])
    msg = view.respond(locals(), "%s/new_campaign.msg" % (lang,),
                           From="%s@%s" % (service_address, server_name),
                           To=message['from'],
                           Subject=view.render(locals(),
                                               "%s/new_campaign.subj"  % (lang,)))

    logging.debug(u"MESSAGE to %s@%s from %s, new campaign %s - %s" %
                      (service_address, server_name,
                           safe_unicode(message['from']),
                      str(cid), campaign_name))
    send_or_queue(msg, cid)
    return
Beispiel #17
0
def test_render():
    # try with some empty vars
    text = view.render({}, "template.txt")
    assert text
Beispiel #18
0
def DICE(message, rollid, host=None):
    service_address = 'pm-dice-' + rollid
    server_name = server_name_config

    # check the sender is known
    sender = c.place_sender(message)
    if sender == INTERNAL:
        logging.debug(u"INTERNAL ignoring %s@%s from %s" %
                      (service_address, server_name, message['from']))
        return # ignore    
    if sender == UNKNOWN:
        if silent:
            logging.debug(u"UNKNOWN ignoring %s@%s from %s" %
                      (service_address, server_name, message['from']))
            return
        raise SMTPError(550, "Unknown sender")

    cid = sender[0]
    lang = c.campaign_language(cid)

    # verify the rollid exists and belongs to the sender
    roll_obj = find_roll(rollid)

    if roll_obj is None:
        # croak no roll
        msg = view.respond(locals(), "%s/roll/no_roll.msg" % (lang,),
                            From="%s@%s" % (service_address, server_name),
                            To=message['from'],
                            Subject=view.render(locals(),
                                                    "%s/roll/no_roll.subj" % (lang,)))        
        send_or_queue(msg, cid)
        return
    if roll_obj is True:
        # croak already rolled, return result
        ( roll, check, roll_str ) = execute_roll(rollid)
        msg = view.respond(locals(), "%s/roll/already_rolled.msg" % (lang,),
                            From="%s@%s" % (service_address, server_name),
                            To=message['from'],
                            Subject=view.render(locals(),
                                                    "%s/roll/already_rolled.subj" % (lang,)))
        send_or_queue(msg, cid)
        return

    ( roll_cid, roll_character, roll_str ) = roll_obj

    if str(roll_cid) != str(cid):
        # croak spurious ID
        msg = view.respond(locals(), "%s/roll/no_roll.msg" % (lang,),
                            From="%s@%s" % (service_address, server_name),
                            To=message['from'],
                            Subject=view.render(locals(),
                                                    "%s/roll/no_roll.subj" % (lang,)))
        send_or_queue(msg, cid)
        return
    
    if not sender[1] and safe_unicode(roll_character).lower() != safe_unicode(sender[2]):
        # croak not the right person for this roll
        you_are = sender[2]
        intended_for = roll_character
        msg = view.respond(locals(), "%s/roll/wrong_person.msg" % (lang,),
                            From="%s@%s" % (service_address, server_name),
                            To=message['from'],
                            Subject=view.render(locals(),
                                                    "%s/roll/wrong_person.subj" % (lang,)))
        send_or_queue(msg, cid)
        return

    # perform the roll
    ( roll, check, roll_str ) = execute_roll(rollid)

    # report back to the sender and GM (if they are different)
    (gm_address, gm_full, attribution) = c.campaign_gm(cid)

    # GM email
    msg = view.respond(locals(), "%s/roll/rolled.msg" % (lang,),
                           From="%s@%s" % (service_address, server_name,),
                           To=gm_full,
                           Subject=view.render(locals(),
                                                   "%s/roll/rolled.subj"  % (lang,)))
    if not sender[1]:
        msg['cc'] = '%s@%s' % (roll_character, server_name,)
        
    send_or_queue(msg, cid)

    if not sender[1]:
        character = c.get_character(cid, sender[2])
        attribution = c.get_attribution(character['controller'])
        
        msg = view.respond(locals(), "%s/roll/rolled.msg" % (lang,),
                               From="%s@%s" % (service_address, server_name,),
                               To= formataddr( (attribution, character['controller']) ),
                               Subject=view.render(locals(),
                                                       "%s/roll/rolled.subj"  % (lang,)))
        msg['cc'] = 'gm@%s' % (server_name,)
        send_or_queue(msg, cid)
        
    return
Beispiel #19
0
def ROLL(message, host=None):
    service_address = 'pm-roll'
    server_name = server_name_config
    
    sender = c.place_sender(message)
    if sender == INTERNAL:
        return # ignore    
    if sender == UNKNOWN:
        if silent:
            return
        raise SMTPError(550, "Unknown sender")
    
    cid = sender[0]
    lang = c.campaign_language(cid)
    
    if not sender[1]:
        # croak only GMs can ask for rolls
        msg = view.respond(locals(), "%s/roll/not_gm.msg" % (lang,),
                               From="%s@%s" % (service_address, server_name,),
                               To=message['from'],
                               Subject=view.render(locals(),
                                                    "%s/roll/not_gm.subj"  % (lang,)))
        send_or_queue(msg, cid)
        return
    
    (gm_address, gm_full, attribution) = c.campaign_gm(cid)

    # find the recipient that is a PC (if none, internal roll from the GM)
    recipients = c.get_recipients(message)
    character = None
    for recipient in recipients:
        if recipient == 'gm':
            continue
        if recipient == service_address:
            continue
        this_character = c.get_character(cid, recipient)
        if not int(this_character['is_npc']):
            if character is not None:
                # croak only one character per roll
                msg = view.respond(locals(), "%s/roll/too_many_characters.msg" % (lang,),
                            From="%s@%s" % (service_address, server_name,),
                            To=gm_full,
                            Subject=view.render(locals(),
                                                   "%s/roll/too_many_characters.subj"  % (lang,)))
                send_or_queue(msg, cid)
                return
            character = recipient

    # find the roll commands
    rolls = []
    full_content = message.body()
    text = safe_unicode(full_content)
    lines = text.split('\n')
    for line in lines:
        if line.startswith('ROLL:'):
            rolls.append(line[len('ROLL:'):])
    if len(rolls) == 0:
        # croak no rolls
        msg = view.respond(locals(), "%s/roll/no_rolls.msg" % (lang,),
                            From="%s@%s" % (service_address, server_name,),
                            To=gm_full,
                            Subject=view.render(locals(),
                                                   "%s/roll/no_rolls.subj"  % (lang,)))
        send_or_queue(msg, cid)
        return

    # for each roll command, register a roll-id and send a pm-dice-(rollid) email
    if character is not None:
        full_character = c.get_character(cid, character)
        character = full_character
        attribution = c.get_attribution(character['controller'])
     
    for roll in rolls:
        try:
            hashid = add_roll( cid, 'gm' if character is None else character['address'], roll )
            return_service_address = 'pm-dice-' + hashid
            roll_address = "%s@%s" % (return_service_address, server_name,)

            msg = view.respond(locals(), "%s/roll/to_roll.msg" % (lang,),
                            From="%s@%s" % (service_address, server_name,),
                            To=gm_full if character is None else character['controller'],
                            Subject=view.render(locals(),
                                                   "%s/roll/to_roll.subj"  % (lang,)))
            msg['Reply-To'] = roll_address
        except RollStrParseException as e:
            msg = view.respond(locals(), "%s/roll/syntax_error.msg" % (lang,),
                            From="%s@%s" % (service_address, server_name,),
                            To=gm_full,
                            Subject=view.render(locals(),
                                                   "%s/roll/syntax_error.subj"  % (lang,)))
        send_or_queue(msg, cid)
    return
Beispiel #20
0
def START(message, address=None, host=None):
    server_name = server_name_config
    
    if(address.startswith("pm-")):
        return # ignore
    
    sender = c.place_sender(message)
    if sender == INTERNAL:
        return # ignore    
    if sender == UNKNOWN:
        if silent:
            logging.debug(u"MESSAGE to %s@%s from %s - unknown" %
                      (address, server_name, message['from']))
            return
        raise SMTPError(550, "Unknown sender")

    cid = sender[0]
    # check the message haven't been processed already
    if tst_email_processed(message, cid):
        logging.debug(u"IGNORING processed email %s" % (message['message-id'],))
        return # ignore
    # the message as been set as processed

    lang = c.campaign_language(cid)
    campaign_name = c.campaign_name(cid)
    server_name = server_name_config    
    
    recipients = c.get_recipients(message)
    
    if sender[1]: # GM EMAIL
        # determine if the email is sent as somebody else
        (gm_address, gm_full, attribution) = c.campaign_gm(cid)
         
        send_as = None
        for recipient in recipients:
            if recipient.startswith('as-'):
                if send_as:
                    other = send_as = recipient[3:].lower()
                    msg = view.respond(locals(), "%s/repeated_as.msg" % (lang,),
                                From="gm@%s" % (server_name,),
                                To=gm_full,
                                Subject=view.render(locals(),
                                               "%s/repeated_as.subj"  % (lang,)))
                    send_or_queue(msg, cid)
                    return
                send_as = recipient[3:].lower()

        # validate the as-XYZ is valid or note, otherwise
        if send_as:
            original_send_as = send_as
            send_as = c.get_character(cid, send_as)
        
            if not send_as:
                # reply with error to the GM with list of valid send-as
                all_characters = c.all_characters(cid)
                send_as = original_send_as
        
                msg = view.respond(locals(), "%s/unknown_as.msg" % (lang,),
                                   From="gm@%s" % (server_name,),
                                   To=gm_full,
                                   Subject=view.render(locals(),
                                                "%s/unknown_as.subj"  % (lang,)))
                send_or_queue(msg, cid)
                return

        if send_as:
           sender = formataddr( (send_as['name'], '%s@%s' % (send_as['address'], server_name)) )
        else:
           sender = 'gm@%s' % (server_name,)

        # for each recipient that is not an NPC, generate an email
        # either from the gm or from whomever is send_as to them with
        # cc: to the other characters and send them
        
        # extract the text
        full_content = message.body()
        #TODO check message.base.parts
        
        for recipient in recipients:
            if recipient.startswith('as-'):
                continue
            if recipient.startswith('pm-'):
                continue
            character = c.get_character(cid, recipient)

            if int(character['is_npc']) :
                continue

            if character is None:
                #TODO localize
                full_content = "UNKNOWN: " + recipient
                msg = view.respond(locals(), "%s/base.msg" % (lang,),
                                    From="gm@%s" % (server_name,),
                                    To=gm_full,
                                    Subject=full_content)
                logging.debug(u"INVALID character %s" % (recipient,))
                send_or_queue(msg, cid)
                return # ignore
            
            cc_list = []                    
            for other in recipients:
                if other.startswith('as-'):
                    cc_list.append('gm@%s' % (server_name,))
                elif other.startswith('pm-') and not send_as:
                    # this will be ignored by the server, but notifies
                    # the users unless is a send-as which will
                    # highlight the send-as to the players
                    cc_list.append('%s@%s' % (other, server_name))
                elif other != recipient:
                    other_character = c.get_character(cid, other)
                    if other_character is None:
                        full_content = "UNKNOWN: " + other
                        msg = view.respond(locals(), "%s/base.msg" % (lang,),
                                    From="gm@%s" % (server_name,),
                                    To=gm_full,
                                    Subject=full_content)
                        logging.debug(u"INVALID character %s" % (othert,))
                        send_or_queue(msg, cid)
                        return # ignore
                    
                    cc_list.append(formataddr( (other_character['name'], '%s@%s' % (other,server_name,)) ))
            # sort them
            cc_list = sorted(cc_list)

            attribution = c.get_attribution(character['controller'])
            msg = view.respond(locals(), "%s/base.msg" % (lang,),
                            From=sender,
                            To=formataddr( (attribution, character['controller']) ),
                            Subject=campaign_name)
            if cc_list:
                msg['cc'] = ", ".join(cc_list)
            send_or_queue(msg, cid)
    else:
        short_form = sender[2]
        full_character = c.get_character(cid, short_form)
        
        # change the sender to their character email and send to GM
        (gm_address, gm_full, attribution) = c.campaign_gm(cid)
        
        # cc: for show
        cc_list = []
        
        for recipient in recipients:
            if recipient == 'gm':
                continue
            character = c.get_character(cid, recipient)
            cc_list.append(formataddr( (character['name'], '%s@%s' % (recipient,server_name,)) ))
        # sort them
        cc_list = sorted(cc_list)

        # extract the text
        full_content = sanitize(message.body())
        #TODO check message.base.parts
        msg = view.respond(locals(), "%s/base.msg" % (lang,),
                            From=formataddr( (full_character['name'], "%s@%s" % (short_form, server_name)) ),
                            To=gm_full,
                            Subject="%s: %s" % (campaign_name, full_character['name']))
        if cc_list:
                msg['cc'] = ", ".join(cc_list)
        send_or_queue(msg, cid)
        return
Beispiel #21
0
def end_campaign(cid, purge=True):
    """Ends a campaign, returns the list of email addresses to email,
    plus the code for downloading the log.

    It purges all emails and all information about the campaign.
    """

    full_queue = Router.FULL_QUEUE
    messages = list()

    campaign_name_ = campaign_name(cid)
    lang = campaign_language(cid)
    emails_in_campaign = set()
    attribution_for_email = dict()
    ( gm_email, full_gm, gm_attribution ) = campaign_gm(cid)
    emails_in_campaign.add( gm_email )
    campaign_characters = all_characters(cid)

    for character in campaign_characters:
        if not int(character['is_npc']):
            emails_in_campaign.add(character['controller'])

    for email in emails_in_campaign:
        attribution_for_email[email] = get_attribution(email)

    seen_ids = set()
    for key in full_queue.keys():
        msg = full_queue.get(key)
        if msg is None:
            continue
        if not 'Message-ID' in msg:
            msg_id = key
        else:
            msg_id = get_message_id(msg)
        if msg_id in seen_ids:
            continue
        seen_ids.add(msg_id)

        name, sender = parseaddr(msg['from'])
        if sender in emails_in_campaign:
            msg_epoch = mktime_tz(parsedate_tz(msg['Date']))
            messages.append( { 'epoch' : msg_epoch, 'msg' : msg, 'key' : key } )

    # sort messages by date
    messages = sorted(messages, key=lambda t: t['epoch'])

    # create nonce
    while True:
        nonce = hashlib.sha256("%s-%d" %
                                   (cid, random.randint(0,9001))).hexdigest()[0:10]
        target_zip = "%s/%s.zip" % ( campaigns_report_folder, nonce ) 
        if not os.path.exists(target_zip):
            break
    tmp_folder = "/tmp/" + nonce
    os.mkdir(tmp_folder)

    # First pass, compute statistics
    gm_emails = 0
    gm_emails_as_npcs = 0
    pc_emails = 0
    dice_rolls = 0

    for t in messages:
        msg = t['msg']

        name, sender = parseaddr(msg['From'])
        recepients = get_recipients(msg)
        is_dice = False
        rcpts = list()
        if 'pm-dice' in sender or 'pm-roll' in sender:
            is_dice = True
        else:
            for rcpt in recepients:
                if 'pm-dice' in rcpt or 'pm-roll' in rcpt:
                    is_dice = True
                elif not rcpt.startswith('as-'):
                    rcpts.append(rcpt)
        if is_dice:
            dice_rolls += 1
            t['flags'] = { 'dice' : True }
        else:
            if sender == gm_email:
                gm_emails += 1
                t['flags'] = { 'gm' : True }
                for rcpt in recepients:
                    if rcpt.startswith('as-'):
                        gm_emails_as_npcs += 1
                        t['flags']['as'] = rcpt[3:]
            else:
                pc_emails += 1
                placed = place_sender(msg)
                if len(placed) == 3:
                    short_form = placed[2]
                    t['flags'] = { 'pc' : short_form }
                else:
                    t['flags'] = 'UNKNOWN'
        t['recipients'] = rcpts
        
    #### front matter
    tex = codecs.open(tmp_folder + "/campaign.tex", 'w', 'utf-8')
    md = codecs.open(tmp_folder + "/campaign.md", 'w', 'utf-8')

    tex.write(LOADER.get_template("%s/campaign.tex" % (lang,)).render(locals()))
    # GM, PC players, NPC players, attributions, licence
    
    all_mails = len(messages)
    print_characters = list()
    for character in campaign_characters:
        print_characters.append({ 'type' : ("NPC" if int(character['is_npc']) else "PC"),
            'full_name': character['name'], 'short_form': character['address'],
            'attribution' : (character['alt_attribution'] if 'alt_attribution' in character else attribution_for_email[character['controller']]) })
    # TODO start date, end date
    md.write(view.render(locals(), "%s/campaign.md" % (lang,)))
    md.write('\n\n')

    for t in messages:
        msg = t['msg']

        # header (includes who send it, potentially as-XYZ and to/cc)
        rcpts = ", ".join(t['recipients'])
        if 'dice' in t['flags']:
            md.write(view.render(locals(), "%s/dice_roll.md" % (lang,)))
            md.write(u"\n---------\n")
        elif 'gm' in t['flags']:
            if 'as' in t['flags']:
                md.write(u"%s (GM) → %s\n" % (t['flags']['as'], rcpts))
                md.write(u'------------------------\n')
            else:
                md.write(u"GM → %s\n" % (rcpts,))
                md.write(u'------------------------\n')                
        else:
            md.write(u"%s (PC) → %s\n" % (t['flags']['pc'] , rcpts))
            md.write(u'------------------------\n')

        # md.write(t['key'] + "\n\n")
        # if msg['subject']:
        #    md.write(u"Subject: %s\n\n" % (msg['subject'],))
        
        # content
        full_content = sanitize(msg.body())
        md.write(full_content)
        md.write("\n")

        #TODO typeset email

    md.close()
    tex.close()

    # render to PDF
    if os.path.exists("/usr/bin/pdflatex") and os.path.exists("/usr/bin/pandoc"):
        call("cd %s; /usr/bin/pandoc campaign.md -o messages.tex" % (tmp_folder,), shell=True)
        call("cd %s; pdflatex campaign.tex; pdflatex campaign.tex; pdflatex campaign.tex" % (tmp_folder,), shell=True)

    # render to HTML
    if os.path.exists("/usr/bin/pandoc"):
        call("cd %s; /usr/bin/pandoc campaign.md -o campaign.html" % (tmp_folder,), shell=True)

    # zip source texts + PDF to target_zip
    call("cd /tmp; /usr/bin/zip -r %s %s" % (os.path.realpath(target_zip), nonce), shell=True)

    if purge:
        # delete messages from full queue
        for t in messages:
            full_queue.remove(t['key'])

        # purge campaign from redis
        delete_campaign(cid)

    return nonce
Beispiel #22
0
def test_render():
    # try with some empty vars
    text = view.render({}, "template.txt")
    assert text
Beispiel #23
0
def NEW_CHARACTER(message, host=None, pc_or_npc="n"):
    service_address = 'pm-new-' + ("n" if pc_or_npc else "") + "pc"
    server_name = server_name_config
    
    # check the sender is an active GM, otherwise raise 550
    sender = c.place_sender(message)
    if sender == INTERNAL:
        logging.debug(u"INTERNAL ignoring %s@%s from %s" %
                      (service_address, server_name, message['from']))
        return # ignore
    if sender == UNKNOWN:
        if silent:
            logging.debug(u"UNKNOWN ignoring %s@%s from %s" %
                      (service_address, server_name, message['from']))
            return
        raise SMTPError(550, "Unknown sender")
    
    if not sender[1]:
        if silent:
            logging.debug(u"NOT_GM ignoring %s@%s from %s" %
                      (service_address, server_name, message['from']))
            return
        raise SMTPError(550, "Not a GM")
    cid = sender[0]
    lang = c.campaign_language(cid)
    campaign_name = c.campaign_name(cid)
    attribution = c.get_attribution(message['from'])

    # get name of the character from subject, produce short form,
    # ensure short form doesn't collide with existing characters
    full_name = message['subject']

    if '(' in full_name:
        ( full_name, short_form ) = full_name.split('(')
        full_name = full_name.strip()
        if ')' in short_form:
            short_form = short_form.split(')')[0].strip()
    else:
        short_form = full_name.split(' ')[0]
        full_name = full_name.strip()
    short_form = short_form.lower()

    if c.character_exists(cid, short_form):
        all_characters = c.all_characters(cid)
        msg = view.respond(locals(), "%s/repeated_short_form.msg" % (lang,),
                            From="%s@%s" % (service_address, server_name),
                            To=message['from'],
                            Subject=view.render(locals(),
                                                    "%s/repeated_short_form.subj"  % (lang,)))

        logging.debug(u"DUPLICATE short form %s, campaign %s" %
                        (short_form, str(cid)))
        send_or_queue(msg, cid)
        return

    if pc_or_npc:
        # create NPC, associate it with current campaign
        c.new_npc(cid, short_form, full_name)
        all_characters = c.all_characters(cid)

        # return template on the campaign language confirming its creation
        msg = view.respond(locals(), "%s/new_npc.msg" % (lang,),
                            From="%s@%s" % (service_address, server_name),
                            To=message['from'],
                            Subject=view.render(locals(),
                                                    "%s/new_npc.subj" % (lang,)))

        logging.debug(u"NEW_NPC short form %s, campaign %s" %
                        (short_form, str(cid)))
        send_or_queue(msg, cid)
        return
    else:
        # create PC, associate it with current campaign and generate
        # enrollment email
        enrollment_address = c.new_pc(cid, short_form, full_name)
        all_characters = c.all_characters(cid)

        # return template on the campaign language with the enrollment email
        msg = view.respond(locals(), "%s/new_pc.msg" % (lang,),
                            From="%s@%s" % (service_address, server_name),
                            To=message['from'],
                            Subject=view.render(locals(),
                                                    "%s/new_pc.subj" % (lang,)))

        logging.debug(u"NEW_PC short form %s, enroll at %s, campaign %s" %
                        (short_form, enrollment_address, str(cid)))
        send_or_queue(msg, cid)
        return