{"id":20597115,"url":"https://github.com/tradle/bots","last_synced_at":"2025-04-14T23:52:41.233Z","repository":{"id":57167459,"uuid":"80575266","full_name":"tradle/bots","owner":"tradle","description":"[DEPRECATED] Tradle bot framework, allows to drive user interactions in Tradle mobile and web apps and (soon) using smart contracts for critical functions","archived":false,"fork":false,"pushed_at":"2018-09-23T16:46:10.000Z","size":605,"stargazers_count":19,"open_issues_count":0,"forks_count":6,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-14T23:52:22.999Z","etag":null,"topics":["availability","bitcoin","blockchain","bot","bot-framework","ethereum","nodejs","react-native","scalability","serverless","tradle"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tradle.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-01T00:08:01.000Z","updated_at":"2023-01-11T06:38:55.000Z","dependencies_parsed_at":"2022-09-18T16:02:23.328Z","dependency_job_id":null,"html_url":"https://github.com/tradle/bots","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tradle%2Fbots","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tradle%2Fbots/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tradle%2Fbots/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tradle%2Fbots/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tradle","download_url":"https://codeload.github.com/tradle/bots/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248981260,"owners_count":21193144,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["availability","bitcoin","blockchain","bot","bot-framework","ethereum","nodejs","react-native","scalability","serverless","tradle"],"created_at":"2024-11-16T08:20:21.430Z","updated_at":"2025-04-14T23:52:41.216Z","avatar_url":"https://github.com/tradle.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @tradle/bots\n\nDEPRECATED: tradle/serverless is where the future is\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [Digital Identity Intro](#digital-identity-intro)\n- [Examples](#examples)\n- [The pieces of the puzzle](#the-pieces-of-the-puzzle)\n- [Your bot, the Tradle server, and the clients](#your-bot-the-tradle-server-and-the-clients)\n- [Talk to some sample bots](#talk-to-some-sample-bots)\n- [Prerequisites](#prerequisites)\n  - [Platform](#platform)\n  - [Environment](#environment)\n    - [OSX Users](#osx-users)\n  - [Run docker, login](#run-docker-login)\n- [Usage](#usage)\n  - [Clone this repository](#clone-this-repository)\n  - [Install dependencies](#install-dependencies)\n  - [Run Tradle server](#run-tradle-server)\n  - [Create a provider](#create-a-provider)\n  - [Charge Provider's Wallet](#charge-providers-wallet)\n  - [Enable the In-House Bot](#enable-the-in-house-bot)\n  - [Connect your Tradle app](#connect-your-tradle-app)\n    - [Web](#web)\n    - [Mobile](#mobile)\n  - [Configuring your bot](#configuring-your-bot)\n  - [Console](#console)\n    - [Sample Session](#sample-session)\n    - [Console globals](#console-globals)\n  - [Strategies](#strategies)\n    - [Receiving messages](#receiving-messages)\n    - [Sending messages](#sending-messages)\n    - [Creating blockchain seals](#creating-blockchain-seals)\n    - [Events](#events)\n    - [Shared storage](#shared-storage)\n  - [Managing users](#managing-users)\n- [Validation](#validation)\n  - [Models](#models)\n  - [Objects / Resources](#objects--resources)\n  - [Model Builder (still raw)](#model-builder-still-raw)\n- [Known Limitations](#known-limitations)\n- [Contributing](#contributing)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Digital Identity Intro\n\nJump down the [rabbit hole](./docs/mythos.md).\n\n## Examples\n\nSee some existing [bots](./docs/bots.md)\n\n## The pieces of the puzzle\n\nUsers of the Tradle app converse with [providers](#your-bot-the-tradle-server-and-the-clients). The brains behind each provider is a bot.\n\nThe Tradle server takes care of:\n- secure line to your users\n- creation/monitoring of blockchain transactions\n- calling your bot's web server with messages from the user (and blockchain-related events)\n\nThe Tradle app takes care of:\n- cross-platform support (iOS \u0026 Android). iOS is currently more mature.\n- cross-browser support (Chrome, Firefox, Safari, IE11). Chrome currently has the best support.\n- offline support, a must on mobiles, but very hard to develop. You have it here out of the box.\n- UI. You can customize it with per-provider styles on the server-side. In the near future you will be able to add your own JS code that will be executed in the app.\n\nThis framework supports:\n- asynchronous messaging\n- reliable persistent-queue-based send/receive on both the server and the bot ends (the basis for offline support)\n- easy to get started. We provide set of sample bots, which we call \"strategies\"\n\n## Your bot, the Tradle server, and the clients\n\n![communication diagram](./docs/diagram.png \"communication diagram: bots, Tradle server, clients\")\n\nThe Tradle server acts like a router between your bot and your clients, on their Tradle apps. Your bot will represent a single provider, as different providers typically require different functionality. Being able to set up multiple service providers on one Tradle server makes it easier to test and do demos.\n\nFrom the client's perspective (see the Tradle app screenshot below), providers are listed together in the Conversations screen.\n\nIn the guide that follows, you'll set up a service provider called Silly, and connect your bot to it.\n\n![providers in Tradle app](./docs/providers1.png \"Providers as seen in the Tradle app\")\n\n## Talk to some sample bots\n\nAt [https://bots.tradle.io](https://bots.tradle.io) there's a Tradle web app set up with a bunch of test bots, some of which are built with this framework (Inviter and Age Verification). Register, click the chats icon in the Profile page footer and chat with bots.\n\n1. Conversation Screen       | 2. Chatting with the Inviter bot \n:---------------------------:|:---------------------------:\n![](./docs/web-app.png)    |  ![talking to \"Inviter\"](./docs/web-app1.png)\n\n## Prerequisites\n\nHow can you have any pudding if you don't eat your prerequisites?\n\n### Platform\n\nThese instructions have been tested on the following platforms:\n- macOS Sierra\n- Windows 10 Home Edition\n\nIf you run into problems setting up, submit an issue!\n\n### Environment\n\nYou will be using a dockerized Tradle server, and building your bots with Node.js\n\n- [Docker](https://docs.docker.com/engine/installation/). \nOn Windows 10 Home Edition you will need to install Docker Toolbox instead of Docker. \nFollow the Docker link, find Windows installation page and search for Toolbox.\n- [Docker Compose](https://docs.docker.com/compose/install/)\n- [Node.js](https://nodejs.org/en/) 6 or later\n\n#### OSX Users\n\n1. Docker used to run via boot2docker, but now has the much better [Docker for Mac](https://docs.docker.com/docker-for-mac/). Install it. \n2. Remove environment variables in your `~/.bash_profile` that start with `DOCKER_`. These are boot2docker's legacy.\n3. Open a fresh shell. Mm, you smell that? Me neither. boot2docker will plague us no more.\n\n### Run docker, login\n\n1. Create an account on Docker Hub if you haven't already\n2. Make sure Docker is running. To make sure you made sure, run `docker info` and count off two microseconds. If it doesn't spit out some awesome stats, docker is probably not running.\n3. Run `docker login` in your shell, and login with your Docker Hub credentials\n\n## Usage\n\n### Clone this repository\n\n```sh\ngit clone https://github.com/tradle/bots tradle-bots\ncd tradle-bots\n```\n\n### Install dependencies\n\nThis project uses [yarn](https://github.com/yarnpkg/yarn) package manager, which is like [npm](https://github.com/npm/npm), but faster, leaner, and more emoji-infused. You had as at \"emoji\", yarn!\n\nTo install it, smirk ironically, and run: \n\n```sh\nnpm i -g yarn\n# if that doesn't work: sudo npm i -g yarn\n```\n\nThen install dependencies:\n\n```sh\nyarn --ignore-optional\n```\n\n### Run Tradle server\n\nThis uses the Docker Compose file [docker-compose.yml](./docker-compose.yml), at the root of your `tradle-bots` folder:\n\nOn OSX, to enable connecting from the container to the host, run:\n\n```sh\n# https://docs.docker.com/docker-for-mac/networking/#/known-limitations-use-cases-and-workarounds\n#   see: \"I want to connect from a container to a service on the host\"\nsudo ifconfig lo0 alias 10.200.10.1/24\n```\n\nStart the server!\n\n```sh\n# switch to your tradle-bots directory\n# start up dockerized tradle server and web app\nyarn run server\n# check things are running:\ndocker ps\n# ... NAMES\n# ... tradle-web-app\n# ... tradle-server\n```\n\n### Create a provider\n\nLet's create a provider called Silly, with handle `silly` (url path: `/silly`)\n\n```sh\n# attach to the tradle-server container\n# or: `yarn run attach`\ndocker attach tradle-server\n# ( you may need to hit Enter an extra time to show the prompt )\n# you are now in the tradle server's command line client\n# let's create a provider\ntradle-server$ newprovider silly Silly\n# Generating a really good provider: silly \n# This may take a few seconds...\n# Enter a local path or a url of the provider logo:\nhttps://afv.com/wp-content/uploads/2014/11/Tongue.png\n# subscribe your bot's web server for webhooks\n# OSX: see the previous section for the explanation for the IP address value\ntradle-server$ newwebhook silly http://10.200.10.1:8000\n# Windows: use Docker's NAT address (10.0.75.1) to configure webhook:\ntradle-server$ newwebhook silly http://10.0.75.1:8000 \n# start things up\ntradle-server$ restartproviders\n```\n\nYour Tradle server is now running at `http://localhost:44444`, and `silly` provider is running at `http://localhost:44444/silly`\n\n*Note: when attached to a Docker container, if you hit Ctrl+C, you will kill it. Docker Compose will automatically restart it (see the `restart: always` flag in [docker-compose.yml](./docker-compose.yml)), but to be nice, detach with `Ctrl+P Ctrl+Q`*\n\n### Charge Provider's Wallet\n\n`restartproviders` will print your provider's wallet's address, e.g.:\n\n```sh\n# network: ethereum rinkeby\n# silly Balance: 0\n# silly : Send coins to 0x0000000000000000000000000000000000000000\n```\n\nIn order for the provider to write seals to the blockchain, you need to charge this address, for example from this [faucet](https://faucet.rinkeby.io)\n\nWhen seals are written to the blockchain, you'll see something like this in the tradle-server console:\n\n```sh\n#  bank silly +2s wrote chain-seal for tradle.Verification in tx with id 0x5ddc1bfbb4d31adc2efdc487e9edfd843d54a7323b349970f646ed0fc18e59b8\n#  bank silly +2s wrote chain-seal for tradle.PersonalInfo in tx with id 0x9ab91f52871b79d81d768f2b3e9125a146094bad479ff519a8222fc7912b24ad\n```\n\nA few minutes later, the customer will be able to see the same seal in the Data Security section of sealed object (e.g. a form or verification). The app typically syncs once every 5-10 minutes. Refresh it to force a sync.\n\n![](images/data-security.png?raw=true)\n\n### Enable the In-House Bot\n\nBy default, the in-house bot is off. For example, you can enable it and enable a product:\n\n```sh\n# turn on the auto-prompter which will guide you through product purchasers\ntradle-server$ autoprompt silly true \n# auto-verify forms submitted by the customer\ntradle-server$ autoverify silly true\n# enable some products\ntradle-server$ enableproduct silly tradle.CurrentAccount\ntradle-server$ enableproduct silly tradle.MortgageProduct\n```\n\nSee [docs](https://github.com/tradle/server-cli) on configuring your provider\n\n### Connect your Tradle app\n\n#### Web\n\n1. Make sure `docker ps` shows `tradle-web-app` running\n2. Open `http://localhost:55555` in Google Chrome. Firefox, Safari and IE11 are almost there.\n\nNote: by default, the dockerized web app connects to your dockerized Tradle server. If you want to point it elsewhere, add a `.env` file in your tradle-bots directory, e.g.:\n\n```sh\nDEFAULT_TRADLE_SERVER_URL=https://botserver.tradle.io\n```\n\nYou can also add additional Tradle servers to your web/mobile app from the app UI (see below, Mobile #3)\n\n#### Mobile\n\n1. Make sure your phone is on the same network as the computer running your Tradle server.\n2. Get your computer's local ip.\n3. In your Tradle app, on the Conversations screen, click the red button, and choose Add Server URL. Enter the address of your Tradle server: `http://{your_local_ip}:44444`\n\n### Configuring your bot\n\nNo `silly` provider is complete without a silly strategy. Below is the annotated default config file, which can be found at [./sample-conf.json](./sample-conf.json). It runs the strategy in [./lib/strategy/silly.js](./lib/strategy/silly.js). Once you outgrow the `silly` strategy (it took me years), and you've sampled the others in [./lib/strategy](./lib/strategy), feel free to create your own. To use a particular config file, run `yarn start` as follows:\n\n```sh\n# nerds:\n#   the extra '--' after yarn start is to help yarn distinguish its own arguments\n#   from arguments to the underlying script (./cmd.js)\n#   it is equivalent to: DEBUG=tradle:* ./cmd.js --conf ./path/to/your/config.json\nyarn start -- --conf ./path/to/your/config.json\n```\n\n```js\n{\n  // the port on which your bot will run its web server\n  // when you register your webhooks on the Tradle server, specify this port\n  \"port\": 8000,\n  // the console prompt, in this case a red hot chili pepper icon\n  \"repl\": \"\\uD83C\\uDF36  \",\n  // the url of the provider your bot controls on the Tradle server\n  // url format: http://localhost:44444/{providerHandle}\n  \"providerURL\": \"http://localhost:44444/silly\",\n  // the directory in which your bot will store its databases\n  // and any temporary files\n  \"dir\": \"./storage/silly\",\n  // strategies your bot will use when it starts\n  \"strategies\": [\n    \"./lib/strategy/silly.js\"\n  ]\n}\n```\n\n*Note: If running multiple bots simultaneously, be sure to use a different `port` and a different `dir` for each.*\n\n### Console\n\nThe easiest way to get started is by playing in the Javascript console. Make sure your Tradle server us up and [running](#run-tradle-server). \n\nThe console can be started by running `yarn start`. Below is a sample session. Below that, see an outline of the objects and functions available in the global scope.\n\n#### Sample Session\n\n```sh\n# switch to your tradle-bots directory\n#\n# yarn start runs ./cmd.js with lots of logging. See \"scripts\" in package.json\n$ yarn start -- --conf ./conf/silly.json\n# Listening on port 8000\n# \n# before anything test the connection to your provider:\nhealth()\ntesting connection to provider...\nall good!\n# list stored users\nbot.users.list()\n# no users yet\n{}\n# list our strategies\nbot.strategies.list()\n# we're using the silly strategy (see './lib/strategy/silly.js')\n# depending on your config (sample-conf.json), you may be using a different one\n[ [Function: sillyStrategy] ]\n# screw that for now, we want to talk to our users manually\nbot.strategies.clear()\nbot.strategies.list()\n[]\n# print to console all received message\ntogglePrintReceived()\n# go to your Tradle app and say something to the provider your bot's hooked up to\n# ..yay, we got a message\n#  a7d454a8ec9a1bd375f9dd16afdadff9ed8765a03016f6abf7dd10df0f7c8fbe {\n#  \"_s\": \"CkkKBHAyNTYSQQQkBY3Zz1lTCpyGK4aQzW8mzp8cz7KuvP0U9Km8vddXuL8PFnHpeFN60seFpmvGTAmy0hpA4hg/zQVsYXc2h8kIEkcwRQIgdQy4DkLs3AcYZ+LsbZvEyGNbuLzuyNHri1kWuvN3Su8CIQC6TwkhBqyJn+QG5gUFFFmnxZS+iI0OJ2yQIB4I2dGhbA==\",\n#  \"_t\": \"tradle.CustomerWaiting\",\n#  \"_z\": \"ac1c730a4b803b9cb9ca88c6ed0ddadce06d89e5f881f4c91f76e64050728a4c\",\n#  \"message\": \"Ove has entered the chat\",\n#  \"time\": 1486070892140\n}\n# list stored users\nbot.users.list()\n# ok, this is that person that was messaging us earlier\n# { a7d454a8ec9a1bd375f9dd16afdadff9ed8765a03016f6abf7dd10df0f7c8fbe: \n#   { id: 'a7d454a8ec9a1bd375f9dd16afdadff9ed8765a03016f6abf7dd10df0f7c8fbe',\n#     history: [ [Object], [Object], [Object], [Object], [Object], [Object] ],\n#     profile: { firstName: 'Ove' } } }\n# ok, this is the guy who was messaging us earlier\n# let's say hi (make sure to replace a7d4... with the 'id' that bot.users.list() printed out)\nbot.send({ userId: 'a7d454a8ec9a1bd375f9dd16afdadff9ed8765a03016f6abf7dd10df0f7c8fbe', object: 'hey Ove!' })\n# ok, good chat, let's turn the Silly strategy back on. \n# Silly will send a message to the app, and you can chat with Silly, \n# but do not expect any serious stuff, be silly yourself\nbot.strategies.use(strategies.silly)\n# if you build your own strategy, you simply use require(..):\n#   bot.strategies.use(require('./path/to/my/strategy'))\n# or add the path to your own conf file's \"strategies\" field\n```\n\n#### Console globals\n\nas you can see in the session above, the console exposes a bunch of objects and functions in the global scope:\n\n```\n- health                      [Function]    test the connection to your provider\n- togglePrintReceived         [Function]    toggle the printing to console of received messages\n- bot                         [Object]\n  - bot.strategies            [Object]\n    - bot.strategies.list     [Function]    list enabled strategies\n    - bot.strategies.use      [Function]    enable a strategy\n    - bot.strategies.disable  [Function]    disable a strategy\n    - bot.strategies.clear    [Function]    disable all strategies\n  - bot.users                 [Object]\n    - bot.users.list          [Function]    list users\n    - bot.users.get           [Function]    get a user's state by id\n    - bot.users.del           [Function]    delete a user\n    - bot.users.clear         [Function]    delete all users\n    - bot.users.new           [Function]    create a new user (you probably don't need this)\n  - bot.seals                 [Object]\n    - bot.seals.list          [Function]    list seals\n    - bot.seals.get           [Function]    get a seal by an object link\n    - bot.seals.queued        [Function]    get queued seals\n  - bot.queued                [Object]\n    - bot.queued.seals        [Function]    list queued seals (same as bot.seals.queued())\n    - bot.queued.send         [Function]    list queued sends\n    - bot.queued.receive      [Function]    list queued receives\n  - bot.send                  [Function]    send a message to a user\n  - bot.shared                [Object]      basic shared storage\n    - bot.shared.set          [Function]    set key-value mapping\n    - bot.shared.get          [Function]    get value by key\n```\n\n### Strategies\n\nYadda yadda, the examples were fun, now how do I build my own bot?\n\nImplementing a basic strategy for a bot is easy. See [./lib/strategy](./lib/strategy) for examples. Below is the echo strategy, which echoes everything any given user says back to them (and boy, do users love it).\n\n```js\n// ./lib/strategy/echo.js\nconst { co } = require('bluebird').coroutine\n\nmodule.exports = function echoStrategy (bot) {\n  return bot.addReceiveHandler(co(function* ({ user, object, link /*, other goodies*/ }) {\n    // we received `object`\n    // send it back\n    yield bot.send({ userId: user.id, object })\n  }))\n}\n```\n\nIf your Promises are a bit rusty, or if you're asking yourself \"what's `co`?\" or \"isn't `yield` only for generators?\", skim [this](./docs/promises.md)\n\n[./lib/strategy/silly.js](./lib/strategy/silly.js) is a slightly more complex strategy, and [tradle/bot-products](https://github.com/tradle/bot-products) is an expert-system type strategy that is a pared down version of the Tradle server's in-house bot's strategy.\n\n#### Receiving messages\n\nTo handle incoming messages from users, add a receive handler as follows:\n\n```js\nfunction myStrategy (bot) {\n  bot.addReceiveHandler(function ({ user, object, link /*, other goodies*/ }) {\n    // return a Promise to ensure receive order\n  })\n\n  // tip: wrap in `co` to make your async javascript saner:\n  // \n  // const co = require('bluebird').coroutine\n  // bot.addReceiveHandler(co(function* ({ user, object /*, other goodies*/ }) {\n  //   yield promiseSomething()\n  //   yield promiseSomethingElse()\n  // }))\n}\n```\n\n#### Sending messages\n\nTo send a message to a user, use `bot.send({ userId, object })`:\n\n```js\n\nfunction myStrategy (bot) {\n  // const news = ...\n  // ...\n  news.on('raining men', function () {\n    // bot.send(...) returns a Promise. Sensing a theme?\n    bot.send({ \n      userId: String, \n      object: {\n        _t: 'tradle.SimpleMessage'\n        message: 'wear a helmet'\n      }\n      // equivalent shorthand for sending simple messages:\n      // object: 'wear a helmet'\n    })\n  })\n  // ...\n}\n```\n\n#### Creating blockchain seals\n\nObjects sent to a user, or received from a user can be sealed on blockchain as follows. To seal an object, you need to know its `link`, which \n\n```js\nfunction echoAndSealStrategy (bot) {\n  return bot.addReceiveHandler(co(function* ({ user, object, link /*, other goodies*/ }) {\n    yield bot.send({ userId: user.id, object })\n    bot.seal({ link })\n  }))\n}\n```\n\nTo handle seals being written/read from the blockchain:\n```js\n// see https://github.com/tradle/server-cli#sample `wroteseal` event\n// for a full list of available properties\nbot.seals.addOnReadHandler(co(function* ({ link, txId /*, ...*/ }) {\n  // object with link `link` has been detected\n  // on the blockchain in the transaction with id `txId`\n}))\n\n// see https://github.com/tradle/server-cli#sample `wroteseal` event\n// for a full list of available properties\nbot.seals.addOnWroteHandler(co(function* ({ link, txId /*, ...*/ }) {\n  // object with link `link` has been written\n  // to the blockchain in the transaction with id `txId`\n}))\n```\n\n#### Events\n\nthe `bot.users` object emits the following events:\n\n- 'create': a new user state object has been created\n- 'delete': a user state object has been deleted\n- 'clear': all user state has been deleted\n- 'update': a user state object has changed\n\nthe `bot.seals` object emits the following events:\n\n- 'push': a request to seal an object has been pushed to the Tradle server\n- 'wrote': the Tradle server has written a seal to the blockchain\n- 'read': the Tradle server has read a seal for an object from the blockchain\n\nthe `bot` object emits the following events:\n\n- 'message': when a message has been handled by all enabled strategies without error\n- 'sent': when a message has been sent to the Tradle server for deliver to the client\n- 'seal:push', 'seal:wrote', 'seal:read': re-emitted for convenience from `bot.seals`\n- 'user:create', 'user:delete', 'user:clear', 'user:update': re-emitted for convenience from `bot.users`\n\n#### Shared storage\n\nA basic shared key-value storage option is available at `bot.shared`, e.g.:\n\n```js\nbot.addReceiveHandler(co(function* ({ user, object }) {\n  const excellent = bot.shared.get('excellentUsers') || []\n  if (!excellent.includes(user.id) \u0026\u0026 isExcellentMessage(object)) {\n    // remember who's excellent\n    excellent.push(user.id)\n    bot.shared.set('excellentUsers', excellent)\n    yield bot.send({ \n      userId: user.id, \n      object: 'wow, I just realized...you are excellent!' \n    })\n  }\n\n  // ...\n  // be extra excellent to excellent users\n}))\n\n// ...\n// batch-send excellent users special stuff\n```\n\n### Managing users\n\n`bot.users` is the user manager object, which you can explore in the [console](#console). If you like mostly empty JSON objects, you're going to love this one.\n\nEach user has a single state object, which is accessible with `bot.users.get(userId)`\n\nUsers are automatically registered with a default state object when the first message from them is received:\n\n```json\n{\n  \"id\": \"..userId..\", \n  \"history\": [] \n}\n```\n\nWhen you `bot.send(...)` or when your bot receives messages, they get appended to `state.history`. You can store whatever your evil bot needs on the user state object, just don't forget to `bot.users.save(userState)` lest the evil be thwarted.\n\n## Validation\n\nHow can you have any pudding...wait, we already did that one\n\n### Models\n\n[@tradle/validate](http://github.com/tradle/validate)\n\nValidate your models. To be renamed to `@tradle/validate-model` once it has enough deps to break things.\n\n### Objects / Resources\n\n[@tradle/validate-resource](http://github.com/tradle/validate-resource) \n\nValidate objects against their corresponding models.\n\n### Model Builder (still raw)\n\n[@tradle/build-model](http://github.com/tradle/build-model) \n\nBuild a model in code.\n\n## Known Limitations\n\n- database: for simplicity and ease of getting started, the bot framework uses [lowdb](https://github.com/typicode/lowdb) for its databases. Yes, it's not a production-level database, it writes synchronously to the file-system, etc. Feel free to substitute it with your personal preference once you're past the prototype phase (e.g. the Tradle server uses LevelDB).\n\n## Contributing\n\nPull requests are welcome. If you build a strategy that you would like to share or show off, submit a pull request to add it to this README.\n\nThe [master](https://github.com/tradle/tradle-bots) branch has stable if outdated code. The [dev](https://github.com/tradle/bots/tree/dev) branch has the latest developments, so pull requests are best submitted there.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftradle%2Fbots","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftradle%2Fbots","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftradle%2Fbots/lists"}