hoprnet / hopr-chatbot Goto Github PK
View Code? Open in Web Editor NEWSimple bot on top of a HOPR node able to listen and reply to messages using a gRPC server.
Simple bot on top of a HOPR node able to listen and reply to messages using a gRPC server.
All bots will require to read data from the Twitter API to verify some actions. Of course, these actions depend on the bot, but in most cases, we'll need to ensure the hoprnet
is mentioned, as well as specific tags. We need a generic interface that can be extended by the bots in a generic fashion.
parseTweet(url: TweekLink) => { user_mentions, hashtags, content }
: Method able to obtain the relevant data from a given week.onTweet(callback: ChatPromise(res:ChatBotSuccess => ChatMessage, rej:ChatBotFailure => ChatMessage) => ())
: Method able to be hooked to a generic ChatPromise
which resolves to a specific ChatMessage
given a condition within the chatbot.For our Week 3, we'll be creating Bouncer Bot. Given an outline, we now need to implement its actions for it to be fully functional.
xHOPR
tokens.There are two markdown links referring to hopr-server both of them are incorrect
Requires #48. The way coverbot
has been verifying users is by sending a message to itself in the following manner:
coverbot.send(coverbot, message, [node])
You can actually see how coverbot sends those messages in https://github.com/hoprnet/hopr-chatbot/blob/feature/basodino/src/bots/coverbot/index.ts#L322. The issue of that approach is that due to the way we encode messages, any node could "pretend" being coverbot. In reality, a closer analysis to the way coverbot is verifying nodes is by decoding the messages and seeing the actual method as follows:
coverbot.send(coverbot, "$coverbot: verifying $node", [node])
This means that any node could actually do the following:
node.send(coverbot, "$coverbot: verifying $node", [])
which would be picked up in https://github.com/hoprnet/hopr-chatbot/blob/feature/basodino/src/bots/coverbot/index.ts#L406 as "coverbot successfully used $node" for relaying, which in reality would be a node that sent a message with settings includeRecipient false
with the content "$coverbotAddress: verifying $myOwnNode".
This was a known 🐛 in the bot internally, which was up for grabs during our last session 👀 . As we will be using additional functionality for incoming sessions, we'll be patching that now. From now on, the "listener" of a message for coverbot upon successful relay (i.e. this line) will not only check that the message sent to it "comes from"* a coverbot
address, but also that the contents have the $SECRET
created for that particular node.
$SECRET
value create by the coverbot (see #48) and include it as part of the message.message.from
but also that message.text
includes the $SECRET of that particular coverbot. Since messages can be sent as a request from other coverbot, make sure to read our database bots "table" and compare the contents of $SECRET such as bots[message.from] === getSecret(message.text)
A
or B
. This is by design, but could be changed in the future and/or implemented on an application layer (as we are currently doing with coverbot with $SECRET, although there are of course better implementations).As part of the new relay schema, coverbot
s can send other coverbot
s messages to "ask" them to try and relay a message on the original's coverbot
behalf. This process can be completed as much as N
times, and looks something as follows:
To avoid fake coverbot
s to trigger calls from nodes that are not HOPR Services AG bots, we'll store the address of the "official" coverbots into our database. We'll then ask coverbot to only listen to relays from messages that come from these registered coverbot
s.
bots
the following key-value entry {$NODE: $SECRET}
where $NODE
is the this.address
of the bot and $SECRET
is a randomly generated numerical number. This value can be rewritten as many times, as will be read before validating any relay.Upon completing this task, here's what the Firestore database could look like:
database
\_ basodino-v2-develop-steven
\_ score = {...}
\_ state = {...}
\_ bots
\_ { 16Uiu2HAkzz6ciBeuPCJ4mL3BCgbPhW4SyRwn8jd4gv8Q8w3P2Q5W: 939926 }
As part of our community calls, we are constantly building games and bounties to showcase the uses of the HOPR protocol. Bounties are short-lived, asynchronous, specific incentivized activities that are run bi-weekly using eurogame-like dynamics.
BouncerBot is a chatbot built on top of the HOPR Protocol that invites Bounty users to interact with HOPR Nodes using HOPR Chat. Bouncerbot goals are to engage users, teach them about the commands available in HOPR Chat, while showcasing the dynamics of the protocol where the content can be hidden or not upon request.
We'll be looking to ask people to do activities through chatbot as part of the dailies.
daily
mode
daily $day
shows the timeframe where the bot can listen to you, whether the daily is completed and the time left.daily $day view
shows a list of actions, (e.g. open a payment channel to me) and whether message.from
completed it.daily $day verify $action
prompts the bot to execute an action that verifies the user completes everything.daily $day url
returns the URL where that daily results can be seen by anyone.For the round two of basodino, we'll need to copy the score
"table" from the basodino
"database" from our Firestore actual database, and restore them with a score of 100
. This process should only run once if the "database" value for the score
"table" is empty.
coverbot/constants.ts
an array of the possible "databases" we can switch/base our scores from. Right now the value is ["basodino","basodino-develop"]
COVERBOT_DATABASE_RESTORE_SCORE_FROM
env in our env.ts
, with possible values being grabbed from coverbot/constants.ts
coverbot
checks if its score
"table" is empty AND COVERBOT_DATABASE_RESTORE_SCORE_FROM
is different from null
. If so, then it copies the score
"table" from COVERBOT_DATABASE_RESTORE_SCORE_FROM
's value, and iterates over them to make them 100
before updating the new score
table.score
"table" exists, then nothing is done.Although implementation is suggested, another alternative is welcomed. The goal is provided in the task subject. Also, the terms "table", "database" and database are used to express the NoSQL nature of our score key-value entry, where:
database (firestore)
\_ "database" (network, e.g. basodino)
\_ "table" (i.e score or state)
cloudconfig.yml
file to allow creating Docker image for chatbot upon master merge.hoprassociation
As we'll have now multiple coverbot
s writing into our score database, we need to ensure we increase our node scores atomically, as otherwise if N
bots write on a given node at the same time, instead of increasing their score by relay_points * N
points, we'll be doing relay_points
only.
score.Relayed
constant (seen used here) to select the amount to increase.We need to ensure that the bots are able to pay back bounties given specific rules are completed. To do so, we'll be using the LinkDrop SDK to manage the creation of the payment based on the run-time parameters, by creating an interface each bot can extend. Each bot will be able to provide links with ERC-20 or Ether in them, and whenever they are deployed, initialize the adequate contract calls as to budget their rewards.
IPayable
which adds the following methods and objects to the bot:
setupSDK()
: Method able to read from ENV
to initialize the LinkDrop SDK and fail if it misses a param.setupProxy(key: EthereumPrivateKey, campaignId: "$BOT_NAME", budget: number )
: Method able to deploy a proxy contract with a given budget to be used for generating the rewards.rules: { "rewardees": number, "reward": number }
: The rules of engagement for the bot to know how many bounties it can pay per person.payBounty(address: HOPRAddress)
: Method to pay the bounty which generates a LinkDrop link and sends it to the HOPR Address.It's important to be able to provide network and other parameters for the LinkDrop SDK. Make sure to add these as environment variables.
In short, the server seems to welcome any node but might not learn about it until later when trying to send a message. Whoever, it could timeout before sending, not allowing it to crawl and thus failing to reach the original sender.
10 xDAI
in them.10 xHOPR
tokens which will be slowly dripped into the connected node.For context, a coverbot
does the following actions:
To increase availability in round 2, we need to modify coverbot
to use other coverbot
as to relay connections whenever needed. The most common case, is when coverbot
can not reach a node on a first try on 3
. Whenever coverbot
fails to do 3
to a node (e.g. alice
), coverbot
can try the next round to use one of the support coverbot
to be able to reach alice
instead. In short, this looks something like this:
coverbot.send(coverbot, [alice])
then coverbot.send(coverbot, [alice, support_{RU,JP,US}])
if fails.
Visually, this looks something like this:
We want to make sure only coverbot
uses another coverbot
to relay, so we'll add a separate table in the database where each coverbot
will register its own address to ensure they only listen to other coverbot
s. We will tackle this in a separate ticket though.
relayTimeout
data structure from Map<string, NodeJS.Timeout>
to Map<{coverbot:string,node:string}, NodeJS.Timeout>
. string
is meant to be each respective node address.timeout
in https://github.com/hoprnet/hopr-chatbot/blob/feature/basodino/src/bots/coverbot/index.ts#L290-L299 to avoid giving up and instead, fetch another coverbot address and retry the message.message.from == this.address
but instead for message.from in coverbotAddressArray
For our Week 3, we'll be creating Bouncer Bot. The outline is its background story to it, and the logic that needs to follow, alongside the rules of engagement and when a price needs to be awarded.
bouncerbot
folder that describes bouncerbot outline.All chatbots have common tasks and status that allows them to have generic functions while at the same time having their own specific “personality”. To achieve so, we need to create a Chatbot
interface able to provide a structure for future chatbots, as to ensure they all a similar pattern when possible.
ChatBot
, which provides the general interface a bot needs to overload as to be able to be instantiated. This interface should include the following methods and types and/or a similar structure for instantiating bots on a long term basis (method definition can be changed):
init() => void
: Initial behaviour for the bot to use whenever it starts.type ChatBotStatus: enum
: An enum describing all the possible status the bot can have.type ChatBotCommands: enum
: An enum describing all the methods the bot can have.type ChatBotMessages: enum
: An enum describing all the possible messages the bot can have.state: { [key: ChatStatus]: ChatStatus }
: An object containing an array describing the possible states the bot can go from/to.directory: { [key: HOPRAddress]: number }
: An object mapping interacting users and their messages.lengthwinners[]: HOPRAddress[]
: An array showcasing the participants that have successfully completed the bounty from the bot.commands: { [key: ChatBotCommand]: () => void }
: An array describing which commands are enabledsend(address: HOPRAddress, message: HOPRMessage)
: A method for sending message to a nodestateReducer(address: HOPRAddress, from: ChatStatus, to: ChatStatus)
: A method that is being called whenever the bot changes state for a given HOPRNode, and updates the sate as needed for that user.onMessage() => void
: A method that is being called back when the bot receives a messageonSend() => void
: A method that is called back when the bot sends a message(details will be added by @peterbraden)
BOT_TIMESTAMP
that allows to only "start" bot in such BOT_TIMESTAMP
TIMESTAMP
is generated, and we need to make sure that the tweets do not come earlier than give TIMESTAMP
(avoid reusing tweets).party
rather than look only at the beginning of the message.timeout
to a check of multiple messages.hint
message.Thanks for your work so far on the cluebot, @shresthagrawal !
For the next iteration, let's change the flow slightly so that players can make some limited number of investigations, can make some limited number of guesses, and exactly one accusation, which needs to be made by tweet and parsed by the bot.
I think we should rename this bot Mysterybot, because I would like to reuse it in a later bounty where the name Cluebot doesn't make as much sense.
Implement two modes: bounty mode and fun mode. In bounty mode players must tweet their accusation and can win xDAI if they're among the first winners. In fun mode, the social media interaction is turned off and players can't win xDAI.
Add a new command investigate [room]
. Three[?] times per case, the player can investigate a room. The response will either be to confirm the room is where the crime was committed (if they're investigating the right room) or (in every other room) to eliminate that room and a random incorrect suspect and weapon.
Formalize the commands. So instead of just parsing for suspect, weapon, room, let's have a clearly defined list of inputs:
Rules: Displays a message explaining the object of the game and how many guesses, investigations and accusations you get.
Winners (in bounty mode): Displays the number of winners and the number of prizes remaining.
Guess [suspect] [weapon] [room]: Make a guess at the solution: as now, the bot will respond to explain which items are wrong. If correct, the bot should indicate that the solution is correct, but not move straight to the reward.
Accuse [tweet URL] (in bounty mode): Make a single guess at the solution. If it's right, the player is congratulated and gets their reward. If it's wrong, the game is over.
Accuse [suspect] [weapon] [room] (in fun mode): Make a single guess at the solution. If it's right, the player gets congratulated. If wrong, the game ends. Entering an accusation in this format in bounty mode will prompt the player about tweeting.
Investigate [room]: A new command, explained above.
Help: Lists and explains the commands
Entering a command in the wrong format should trigger a message explaining the correct format.
For our Week 3, we'll be creating Bouncer Bot. As we need to deploy it live in our platform, it's important to provide the instructions on the base minimal requirements for its Docker image for deployment.
docker run
command that has all the environment variables BouncerBot
needs for it to run locally successfully.docker-compose
file that is able to quickly spin up a BouncerBot
within the repository with pre-defined variables.A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.