<commit_before>#!/usr/bin/env python3
import argparse
import logging
import os
import re
import textblob
import tracery
import tweepy
import yaml

from tracery.modifiers import base_english

from fewerror.twitter import auth_from_env, status_url
from fewerror import checkedshirt

SOURCE = os.path.join(os.path.dirname(__file__), 'thatsnotmybot.yaml')
traceryish_rx = re.compile(r'#(\w+)(?:\.\w+)*#')

log = logging.getLogger(__name__)


def fmap(f, val):
    if isinstance(val, list):
        return [fmap(f, x) for x in val]
    else:
        return f(val)


def validate(j):
    used = {'origin', }
    for k, v in j.items():
        def _validate(val, k=k):
            for var in traceryish_rx.findall(val):
                if var not in j:
                    raise ValueError(k, val, var)
                used.add(var)
        fmap(_validate, v)

    unused = j.keys() - used
    if unused:
        raise ValueError(unused)


def modifier_is(noun_phrase):
    '''Extremely crude noun_phrase + {is,are} agreement.'''
    s = textblob.Sentence(noun_phrase)
    _, pos_tag = s.pos_tags[-1]
    verb = ' are' if pos_tag in ('NNS', 'NNPS') else ' is'
    return noun_phrase + verb


class ThatsNotMyBot(object):
    def __init__(self):
        with open(SOURCE, 'r', encoding='utf-8') as f:
            self.source = yaml.load(f, Loader=yaml.CLoader)

        validate(self.source)
        self.grammar = tracery.Grammar(self.source)
        self.grammar.add_modifiers(base_english)
        self.grammar.add_modifiers({'is': modifier_is})

    def generate(self):
        return self.grammar.flatten('#origin#')

    def sample(self, n):
        '''Print out n sample texts'''
        for _ in range(n):
            print(self.generate())

    @staticmethod
    def get_twitter_api():
        auth = auth_from_env()
        api = tweepy.API(auth,
                         wait_on_rate_limit=True,
                         wait_on_rate_limit_notify=True,
                         retry_count=1)
        return api

    def tweet(self, state_filename):
        '''🐦🐦🐦'''
        log.info('Loading state from %s', state_filename)
        try:
            with open(state_filename, 'r', encoding='utf-8') as f:
                state = yaml.load(f)
            log.info('Loaded state %s', state)
        except FileNotFoundError:
            state = {}

        try:
            object_ = state['object']
            last_id = state['last_id']
        except KeyError:
            object_ = self.grammar.flatten('#object#')
            last_id = None

        self.grammar.pop_rules('object')
        self.grammar.push_rules('object', object_)

        n = state.get('n', 0) + 1
        yes = n >= 8

        status = self.grammar.flatten('#{}#'.format('is' if yes else 'not'))
        log.info("Posting “%s”", status)
        if last_id is not None:
            log.info("  in reply to %s", last_id)

        api = self.get_twitter_api()
        r = api.update_status(status, in_reply_to_status_id=last_id)
        log.info("Posted %s", status_url(r))

        if yes:
            state = {}
        else:
            state['object'] = object_
            state['last_id'] = r.id
            state['n'] = n
        log.info('Saving state %s to %s', state, state_filename)<commit_msg>tnmb: no need to push/pop_rules<commit_after>#!/usr/bin/env python3
import argparse
import logging
import os
import re
import textblob
import tracery
import tweepy
import yaml

from tracery.modifiers import base_english

from fewerror.twitter import auth_from_env, status_url
from fewerror import checkedshirt

SOURCE = os.path.join(os.path.dirname(__file__), 'thatsnotmybot.yaml')
traceryish_rx = re.compile(r'#(\w+)(?:\.\w+)*#')

log = logging.getLogger(__name__)


def fmap(f, val):
    if isinstance(val, list):
        return [fmap(f, x) for x in val]
    else:
        return f(val)


def validate(j):
    used = {'origin', }
    for k, v in j.items():
        def _validate(val, k=k):
            for var in traceryish_rx.findall(val):
                if var not in j:
                    raise ValueError(k, val, var)
                used.add(var)
        fmap(_validate, v)

    unused = j.keys() - used
    if unused:
        raise ValueError(unused)


def modifier_is(noun_phrase):
    '''Extremely crude noun_phrase + {is,are} agreement.'''
    s = textblob.Sentence(noun_phrase)
    _, pos_tag = s.pos_tags[-1]
    verb = ' are' if pos_tag in ('NNS', 'NNPS') else ' is'
    return noun_phrase + verb


class ThatsNotMyBot(object):
    def __init__(self):
        with open(SOURCE, 'r', encoding='utf-8') as f:
            self.source = yaml.load(f, Loader=yaml.CLoader)

        validate(self.source)
        self.grammar = tracery.Grammar(self.source)
        self.grammar.add_modifiers(base_english)
        self.grammar.add_modifiers({'is': modifier_is})

    def generate(self):
        return self.grammar.flatten('#origin#')

    def sample(self, n):
        '''Print out n sample texts'''
        for _ in range(n):
            print(self.generate())

    @staticmethod
    def get_twitter_api():
        auth = auth_from_env()
        api = tweepy.API(auth,
                         wait_on_rate_limit=True,
                         wait_on_rate_limit_notify=True,
                         retry_count=1)
        return api

    def tweet(self, state_filename):
        '''🐦🐦🐦'''
        log.info('Loading state from %s', state_filename)
        try:
            with open(state_filename, 'r', encoding='utf-8') as f:
                state = yaml.load(f)
            log.info('Loaded state %s', state)
        except FileNotFoundError:
            state = {}

        try:
            object_ = state['object']
            last_id = state['last_id']
        except KeyError:
            object_ = self.grammar.flatten('#object#')
            last_id = None

        n = state.get('n', 0) + 1
        yes = n >= 8

        status = self.grammar.flatten(
            '#[object:{}]{}#'.format(
                object_,
                'is' if yes else 'not'))
        log.info("Posting “%s”", status)
        if last_id is not None:
            log.info("  in reply to %s", last_id)

        api = self.get_twitter_api()
        r = api.update_status(status, in_reply_to_status_id=last_id)
        log.info("Posted %s", status_url(r))

        if yes:
            state = {}
        else:
            state['object'] = object_
            state['last_id'] = r.id
            state['n'] = n
        log.info('Saving state %s to %s', state, state_filename)
{'fraction_non_alphanumeric': 0.125591332214253, 'fraction_numerical': 0.002441629787883412, 'mean_word_length': 2.0900518623290902, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6782460', 'n_tokens_mistral': 2165, 'n_tokens_neox': 2080, 'n_words': 503}