diff --git a/packages/dvilela/agents/memeooorr/aea-config.yaml b/packages/dvilela/agents/memeooorr/aea-config.yaml index 3e003cc..c9b98e7 100644 --- a/packages/dvilela/agents/memeooorr/aea-config.yaml +++ b/packages/dvilela/agents/memeooorr/aea-config.yaml @@ -41,8 +41,8 @@ skills: - valory/transaction_settlement_abci:0.1.0:bafybeigh2vkt74jrad5gtsczrgqcuhcqe7jkgjy7jdw56yamlzwwnaymjy - valory/registration_abci:0.1.0:bafybeib3n6vqkfbrcubcbliebjnuwyywdinxkbzt76n6gbn2kg7ace47dq - valory/reset_pause_abci:0.1.0:bafybeihkj6lmaypspyxe5qqrjgnolyck62pyvqoylr24ab6ue4steqcw7e -- dvilela/memeooorr_abci:0.1.0:bafybeihhww5iuja5kqwoyxa7lqotrurmrjzr7sknbnlbjwos5usjx3smfu -- dvilela/memeooorr_chained_abci:0.1.0:bafybeif4ksllugg5mjtvd27sregw46r4qdaazpixt4k4kggfeqml2mhgiq +- dvilela/memeooorr_abci:0.1.0:bafybeiby32vi6b4umzfu2jlbzxjetqmns4q6ycl4te2mxo3x3hbzcaac5i +- dvilela/memeooorr_chained_abci:0.1.0:bafybeiaajpumsm7nuiy6oqfaa6gddbpch7onwcwqt6xufme2v3ozswodpi default_ledger: ethereum required_ledgers: - ethereum diff --git a/packages/dvilela/services/memeooorr/service.yaml b/packages/dvilela/services/memeooorr/service.yaml index 0771df6..a5f4525 100644 --- a/packages/dvilela/services/memeooorr/service.yaml +++ b/packages/dvilela/services/memeooorr/service.yaml @@ -7,7 +7,7 @@ license: Apache-2.0 fingerprint: README.md: bafybeiaekcmielp6mb4qvmo2twwkpmoun36bqajrh7wnnkcpdnia45ycl4 fingerprint_ignore_patterns: [] -agent: dvilela/memeooorr:0.1.0:bafybeifjex7wnvqgueilah64j3ds6g3w524sam2fji5i355zvcziz6a3lm +agent: dvilela/memeooorr:0.1.0:bafybeicvcqdwvfy3gvhu4ng35wbahr7lqfbe6jinzu2nh3z6aaelwonsza number_of_agents: 1 deployment: agent: diff --git a/packages/dvilela/skills/memeooorr_abci/behaviour_classes/twitter.py b/packages/dvilela/skills/memeooorr_abci/behaviour_classes/twitter.py index 5786983..40a8302 100644 --- a/packages/dvilela/skills/memeooorr_abci/behaviour_classes/twitter.py +++ b/packages/dvilela/skills/memeooorr_abci/behaviour_classes/twitter.py @@ -21,6 +21,7 @@ import json import re +import secrets from datetime import datetime from typing import Dict, Generator, List, Optional, Tuple, Type, Union @@ -82,11 +83,18 @@ def async_act(self) -> Generator: """Do the act, supporting asynchronous execution.""" with self.context.benchmark_tool.measure(self.behaviour_id).local(): - latest_tweet = yield from self.decide_post_tweet() + ( + latest_tweet, + feedback_period_max_hours_delta, + ) = yield from self.decide_post_tweet() + self.context.logger.info( + f"feedback_period_max_hours_delta sent to payload: {feedback_period_max_hours_delta}" + ) payload = PostTweetPayload( sender=self.context.agent_address, latest_tweet=json.dumps(latest_tweet, sort_keys=True), + feedback_period_max_hours_delta=feedback_period_max_hours_delta, ) with self.context.benchmark_tool.measure(self.behaviour_id).consensus(): @@ -97,22 +105,56 @@ def async_act(self) -> Generator: def decide_post_tweet( # pylint: disable=too-many-locals self, - ) -> Generator[None, None, Optional[Dict]]: + ) -> Generator[None, None, Tuple[Optional[Dict], int]]: """Post a tweet""" pending_tweet = self.synchronized_data.pending_tweet + feedback_period_max_hours_delta = ( + self.synchronized_data.feedback_period_max_hours_delta + ) + self.context.logger.info( + f"feedback_period_max_hours_delta: {feedback_period_max_hours_delta}" + ) + + # Adjust feedback_period_max_hours with delta + + if feedback_period_max_hours_delta == 0: + while True: + # Fetch or calculate feedback_period_max_hours_delta + feedback_period_max_hours_delta = ( + secrets.randbelow(61) - 30 + ) # Random delta in minutes + self.context.logger.info( + f"Random feedback_period_max_hours_delta: {feedback_period_max_hours_delta}" + ) + # Adjust feedback_period_max_hours with delta + adjusted_feedback_period_max_hours = ( + self.params.feedback_period_max_hours + + (feedback_period_max_hours_delta / 60) + ) + + if adjusted_feedback_period_max_hours >= 0: + break + + adjusted_feedback_period_max_hours = self.params.feedback_period_max_hours + ( + feedback_period_max_hours_delta / 60 + ) + + self.context.logger.info( + f"adjusted_feedback_period_max_hours: {adjusted_feedback_period_max_hours}" + ) # If there is a pending tweet, we send it if pending_tweet: self.context.logger.info("Sending a pending tweet...") - latest_tweet = yield from self.post_tweet(tweet=[pending_tweet]) - return latest_tweet + latest_tweet = self.synchronized_data.latest_tweet or {} + return latest_tweet, feedback_period_max_hours_delta # If we have not posted before, we prepare and send a new tweet if self.synchronized_data.latest_tweet == {}: self.context.logger.info("Creating a new tweet for the first time...") - latest_tweet = yield from self.post_tweet(tweet=None) - return latest_tweet + latest_tweet = yield from self.post_tweet(tweet=[pending_tweet]) or {} + return latest_tweet, 0 # Reset delta to 0 # Calculate time since the latest tweet latest_tweet_time = datetime.fromtimestamp( @@ -122,31 +164,31 @@ def decide_post_tweet( # pylint: disable=too-many-locals hours_since_last_tweet = (now - latest_tweet_time).total_seconds() / 3600 # Too much time has passed since last tweet without feedback, tweet again - if hours_since_last_tweet >= self.params.feedback_period_max_hours: + if hours_since_last_tweet >= adjusted_feedback_period_max_hours: self.context.logger.info( "Too much time has passed since last tweet. Creating a new tweet..." ) - latest_tweet = yield from self.post_tweet(tweet=None) - return latest_tweet + latest_tweet = yield from self.post_tweet(tweet=None) or {} + return latest_tweet, 0 # Reset delta to 0 # If we have posted befored, but not enough time has passed to collect feedback, we wait if hours_since_last_tweet < self.params.feedback_period_min_hours: self.context.logger.info( f"{hours_since_last_tweet:.1f} hours have passed since last tweet. Awaiting for the feedback period..." ) - return {"wait": True} + return {"wait": True}, feedback_period_max_hours_delta # Enough time has passed, collect feedback if self.synchronized_data.feedback is None: self.context.logger.info( "Feedback period has finished. Collecting feedback..." ) - return {} + return {}, feedback_period_max_hours_delta # Not enough feedback, prepare and send a new tweet self.context.logger.info("Feedback was not enough. Creating a new tweet...") - latest_tweet = yield from self.post_tweet(tweet=None) - return latest_tweet + latest_tweet = yield from self.post_tweet(tweet=None) or {} + return latest_tweet, 0 # Reset delta to 0 def prepare_tweet(self) -> Generator[None, None, Optional[str]]: """Prepare a tweet""" @@ -444,6 +486,8 @@ def interact_tweets( # pylint: disable=too-many-locals if action == "none" or str(tweet_id) not in pending_tweets.keys(): continue + # use yield from self.sleep(1) to simulate a delay use secrests to randomize the delay + self.context.logger.info(f"Trying to {action} tweet {tweet_id}") user_name = pending_tweets[str(tweet_id)]["user_name"] diff --git a/packages/dvilela/skills/memeooorr_abci/payloads.py b/packages/dvilela/skills/memeooorr_abci/payloads.py index 3e285e2..a6f0487 100644 --- a/packages/dvilela/skills/memeooorr_abci/payloads.py +++ b/packages/dvilela/skills/memeooorr_abci/payloads.py @@ -38,6 +38,7 @@ class PostTweetPayload(BaseTxPayload): """Represent a transaction payload for the PostTweetRound.""" latest_tweet: Optional[str] + feedback_period_max_hours_delta: int = 0 @dataclass(frozen=True) diff --git a/packages/dvilela/skills/memeooorr_abci/rounds.py b/packages/dvilela/skills/memeooorr_abci/rounds.py index 8a7d4b2..04f8163 100644 --- a/packages/dvilela/skills/memeooorr_abci/rounds.py +++ b/packages/dvilela/skills/memeooorr_abci/rounds.py @@ -143,6 +143,11 @@ def token_action(self) -> Dict: """Get the token action.""" return cast(dict, json.loads(cast(str, self.db.get("token_action", "{}")))) + @property + def feedback_period_max_hours_delta(self) -> int: + """Get the feedback_period_max_hours_delta.""" + return cast(int, self.db.get("feedback_period_max_hours_delta", 0)) + class LoadDatabaseRound(CollectSameUntilThresholdRound): """LoadDatabaseRound""" @@ -184,7 +189,9 @@ def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Event]]: required_class_attributes = () -class PostTweetRound(CollectSameUntilThresholdRound): +class PostTweetRound( + CollectSameUntilThresholdRound +): # pylint: disable=too-many-return-statements """PostTweetRound""" payload_class = PostTweetPayload @@ -193,12 +200,21 @@ class PostTweetRound(CollectSameUntilThresholdRound): def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Event]]: """Process the end of the block.""" - + # pylint: disable=too-many-return-statements # This needs to be mentioned for static checkers # Event.DONE, Event.NO_MAJORITY, Event.ROUND_TIMEOUT if self.threshold_reached: - latest_tweet = json.loads(self.most_voted_payload) + payload = PostTweetPayload( + *(("dummy_sender",) + self.most_voted_payload_values) + ) + latest_tweet_str = payload.latest_tweet + self.context.logger.info( + f"recieved feedback_period_max_hours_delta: {payload.feedback_period_max_hours_delta}" + ) + if latest_tweet_str is None: + return self.synchronized_data, Event.ERROR + latest_tweet = json.loads(latest_tweet_str) # API errors if latest_tweet is None: @@ -208,10 +224,34 @@ def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Event]]: # Wait if latest_tweet.get("wait", False): + self.context.logger.info( + f"Waiting for feedback saved feedback_period_max_hours_delta: {payload.feedback_period_max_hours_delta}" + ) + # update only feedback_period_max_hours_delta in the db + synchronized_data = self.synchronized_data.update( + synchronized_data_class=SynchronizedData, + **{ + get_name( + SynchronizedData.feedback_period_max_hours_delta + ): payload.feedback_period_max_hours_delta + }, + ) return self.synchronized_data, Event.WAIT # Collect feedback if latest_tweet == {} and not feedback: + # update feedback_period_max_hours_delta in the db + self.context.logger.info( + f"Collecting feedbacksaved feedback_period_max_hours_delta: {payload.feedback_period_max_hours_delta}" + ) + synchronized_data = self.synchronized_data.update( + synchronized_data_class=SynchronizedData, + **{ + get_name( + SynchronizedData.feedback_period_max_hours_delta + ): payload.feedback_period_max_hours_delta + }, + ) return self.synchronized_data, Event.DONE # Remove posted tweets from pending and into latest, then reset @@ -222,6 +262,9 @@ def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Event]]: get_name(SynchronizedData.latest_tweet): json.dumps( latest_tweet, sort_keys=True ), + get_name( + SynchronizedData.feedback_period_max_hours_delta + ): payload.feedback_period_max_hours_delta, }, ) return synchronized_data, Event.WAIT @@ -729,7 +772,7 @@ class MemeooorrAbciApp(AbciApp[Event]): final_states: Set[AppState] = {FinishedToResetRound, FinishedToSettlementRound} event_to_timeout: EventToTimeout = {} cross_period_persisted_keys: FrozenSet[str] = frozenset( - ["persona", "latest_tweet", "feedback"] + ["persona", "latest_tweet", "feedback", "feedback_period_max_hours_delta"] ) db_pre_conditions: Dict[AppState, Set[str]] = { LoadDatabaseRound: set(), diff --git a/packages/dvilela/skills/memeooorr_abci/skill.yaml b/packages/dvilela/skills/memeooorr_abci/skill.yaml index 1bd7873..9b533db 100644 --- a/packages/dvilela/skills/memeooorr_abci/skill.yaml +++ b/packages/dvilela/skills/memeooorr_abci/skill.yaml @@ -12,15 +12,15 @@ fingerprint: behaviour_classes/chain.py: bafybeigxfwko3rofqlzyjjhgeo6yabxseqngo2uz5muyfk3f2jjpgdrsli behaviour_classes/db.py: bafybeieuhjes2gsiidepjxcojgnn3swx2znem5uwvz7rwkxpwls3dmlxf4 behaviour_classes/llm.py: bafybeifdhipccrdtfx5vay2sfd7vss4iovwtyeefuz5vrjewjbyatk35c4 - behaviour_classes/twitter.py: bafybeihka7bscxyre2dabp6p5qkb43zvj7l3kdyhujsn55uvt4znnp6uom + behaviour_classes/twitter.py: bafybeidf25i7szjkfj3klfcw22q7ku4enw4k2t3iuxogqkwwfsdkthkhse behaviours.py: bafybeibdbimezloiomvsv3zgfdug4tw276gt7tb5agnezl2nuqcfcplscu dialogues.py: bafybeiaygigeleloplgrsg2sovv463uvzk3zc2tupgazn4ak2vqcangksu fsm_specification.yaml: bafybeif4kr2lnj4ppaaxveej2a3gq2gul6gicu2rar2yeyhg4mspqsikxy handlers.py: bafybeibnnxjczbaeqzxvg4s5mmrogzhgpswwwwpmw6gds6mltgrq27r67y models.py: bafybeidwapsmqst3jw3ahsl4o2lfsapcyn4ojsqn3gz5nvivpsykc4sefy - payloads.py: bafybeibyukr3aewwlbx6c6xe43j6nhgdqwqdgbemswrsvnqp3ftqodvvmi + payloads.py: bafybeifxy5bsv3rovzhzasmrohfnlggpacluigxr6c5fdyux3dwihikpum prompts.py: bafybeiguzllaoupwzyiob6iufx77czkj6a7xe3mdporjtrfnm5m4grzhbe - rounds.py: bafybeiezr7wrjl7k3ojvjqdbpp6vs7u2lmgwdjuhi62sy4hedpe4euwswq + rounds.py: bafybeicjqi4qchvja4n4kyyvhwlblrwr3lfxriiyu3gq6nyt7rpxpvdn3u rounds_info.py: bafybeieepy67c2g7dtkeuueuldhvcpbeavvtkg36gmoempftiic2tqxb4y fingerprint_ignore_patterns: [] connections: diff --git a/packages/dvilela/skills/memeooorr_chained_abci/skill.yaml b/packages/dvilela/skills/memeooorr_chained_abci/skill.yaml index 344f9b8..57a3c75 100644 --- a/packages/dvilela/skills/memeooorr_chained_abci/skill.yaml +++ b/packages/dvilela/skills/memeooorr_chained_abci/skill.yaml @@ -23,7 +23,7 @@ skills: - valory/reset_pause_abci:0.1.0:bafybeihkj6lmaypspyxe5qqrjgnolyck62pyvqoylr24ab6ue4steqcw7e - valory/transaction_settlement_abci:0.1.0:bafybeigh2vkt74jrad5gtsczrgqcuhcqe7jkgjy7jdw56yamlzwwnaymjy - valory/termination_abci:0.1.0:bafybeifi2uodnrjsrivj53g3sjutocmyusbx6mlsb6oanqdyt2mfbyvusy -- dvilela/memeooorr_abci:0.1.0:bafybeihhww5iuja5kqwoyxa7lqotrurmrjzr7sknbnlbjwos5usjx3smfu +- dvilela/memeooorr_abci:0.1.0:bafybeiby32vi6b4umzfu2jlbzxjetqmns4q6ycl4te2mxo3x3hbzcaac5i behaviours: main: args: {} diff --git a/packages/packages.json b/packages/packages.json index 61416e0..094be66 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -4,10 +4,10 @@ "contract/dvilela/service_registry/0.1.0": "bafybeie2rrgzcjehlp2feff6bhkuindxzrnuwxe2jcrsy2thcdtrsp2o24", "connection/dvilela/twikit/0.1.0": "bafybeig2rrxb4v56r7oo3vb5ahgryb7aw6n2i6wreackeamlhndwoldawm", "connection/dvilela/genai/0.1.0": "bafybeidkxxlonrxirznivkmzc34wmby4e4s57rfg2b7k6xyos23g3y6cdy", - "skill/dvilela/memeooorr_abci/0.1.0": "bafybeihhww5iuja5kqwoyxa7lqotrurmrjzr7sknbnlbjwos5usjx3smfu", - "skill/dvilela/memeooorr_chained_abci/0.1.0": "bafybeif4ksllugg5mjtvd27sregw46r4qdaazpixt4k4kggfeqml2mhgiq", - "agent/dvilela/memeooorr/0.1.0": "bafybeifjex7wnvqgueilah64j3ds6g3w524sam2fji5i355zvcziz6a3lm", - "service/dvilela/memeooorr/0.1.0": "bafybeif3fyybh226h2ysgmehj34ozlcvznunxbdceouwgbr4bkwb7hwyry" + "skill/dvilela/memeooorr_abci/0.1.0": "bafybeiby32vi6b4umzfu2jlbzxjetqmns4q6ycl4te2mxo3x3hbzcaac5i", + "skill/dvilela/memeooorr_chained_abci/0.1.0": "bafybeiaajpumsm7nuiy6oqfaa6gddbpch7onwcwqt6xufme2v3ozswodpi", + "agent/dvilela/memeooorr/0.1.0": "bafybeicvcqdwvfy3gvhu4ng35wbahr7lqfbe6jinzu2nh3z6aaelwonsza", + "service/dvilela/memeooorr/0.1.0": "bafybeiemdxl3xh576hcnblljgaohrjcgmpec5mawesc5lwu3ipa7anpmbe" }, "third_party": { "protocol/open_aea/signing/1.0.0": "bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi",