Giter Site home page Giter Site logo

ro31337 / libretaxi Goto Github PK

View Code? Open in Web Editor NEW
3.8K 199.0 825.0 949 KB

Open source Uber #deleteuber

Home Page: https://t.me/libretaxi_bot

License: GNU Affero General Public License v3.0

Shell 2.39% Go 97.61%
telegram ridesharing uber lyft transportation golang

libretaxi's Introduction

LibreTaxi v2

LibreTaxi is open-source Uber proof-of-concept that works through Telegram.

See it in action: https://t.me/libretaxi_bot

Public feed: https://t.me/libretaxi_all

It is closer to Craigslist rideshare rather than Uber, but it works, and works great! The app that is easy to use, supports multiple languages, fast and cool. There are tens of thousands users worldwide, and we're on the way to 1M users. So please spread the word!

Prerequisites

  1. Install Go
  2. Install Go dep
  3. Download the repo to ~/go/src/libretaxi
  4. Install Docker with docker-compose
  5. Run PostgreSQL and RabbitMQ with default credentials (see connection strings below)
docker-compose up -d

Setting up RabbitMQ (for development and production)

rabbitmq:3-management contains UI plugin for queue management. Plugin port is 8080 (15672 in container). Login guest/guest.

Login to RabbitUI here: http://localhost:8080

There is only one queue at the moment:

Note that there is one message producer, and one message consumer threads (goroutines) in application.

Port 5672 is RabbitMQ itself.

LibreTaxi settings

Init settings for ./libretaxi.yml:

telegram_token: YOUR_TOKEN
db_conn_str: postgres://libretaxi:libretaxi@localhost:15432/libretaxi
rabbit_url: amqp://127.0.0.1:8079/
admin_channel_chat_id: -1001324105405
public_channel_chat_id: -1001470847849

Admin channel is the place where you shadow ban spamers. See https://stackoverflow.com/a/41779623/337085 for how to get id for you private channel. You'll need to invite @get_id_bot and type /my_id@get_id_bot. You'll see chat id.

Running

When all services are running, run libretaxi:

dep ensure # or ~/go/bin/dep ensure
go build
./libretaxi

๐ŸŒŸ Project Sponsors

Shown below are our bronze, silver and gold project sponsors. Big thanks to these companies for supporting the project. Note: Listed services are not tested, vetted nor supported by the author(s) in any manner.

Gold sponsors

Prevent workplace conflicts with this Slack app:

Prevent workplace conflicts

Become a sponsor!

View all sponsors

libretaxi's People

Contributors

liftair avatar ro31337 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libretaxi's Issues

Add tests pre-check

Add a script that will run before all the tests. This script should check if localhost.firebaseio.test resolves to 127.0.0.1. If not - it should display message and prevent running the tests suite.

Refactor user validation

User validation (ValidatedUser) can be now refactored. There is no any sense to have this validation as a separate class when we have checkNotNull and checkPlatformType validating mixins. So User class signature can be changed to use these mixins, and ValidatedUser can be removed from app.

Add Response

Response should be implemented as another very essential brick of program architecture. Response is something Action should return. Response can be of type text, or json, or composite, or telegram_menu (text response should be only implemented for now). Response handler(s) will process responses based on response type. For response with type text, a message will be returned to the user.

At least two object should be implemented:

  • Response - abstract class for response, has type property.
  • TextResponse - type property set to text, has message.

Update packages to latest

The following dependencies are satisfied by their declared version range, but the installed versions are behind. Install the latest versions with modifying package file.

 babel-cli                             ^6.7.5  โ†’  ^6.10.1
 babel-eslint                          ^6.0.3  โ†’   ^6.1.1
 babel-plugin-syntax-async-functions   ^6.5.0  โ†’   ^6.8.0
 babel-plugin-transform-regenerator    ^6.6.5  โ†’   ^6.9.0
 babel-preset-es2015                   ^6.6.0  โ†’   ^6.9.0
 babel-preset-stage-2                  ^6.5.0  โ†’  ^6.11.0
 babel-register                        ^6.7.2  โ†’   ^6.9.0
 esdoc                                 ^0.4.6  โ†’   ^0.4.7
 i18n                                  ^0.8.2  โ†’   ^0.8.3
 moment                               ^2.13.0  โ†’  ^2.14.1
 nyc                                   ^6.4.0  โ†’   ^6.6.1
 object-assign                         ^4.0.1  โ†’   ^4.1.0

Add action

Action (or "menu action") represents the action that should be executed when action is selected (by redirect or any other way).

Action can be executed in two ways:

  • with parameter (post method)
  • without parameter (get method)

All actions should have unique identifier so they can be stateful (keep it's state in the database through stateful mixin).

Actions are common way of user interaction, so they should have initialized i18n object.

Add text response handler for cli

There is no such thing as handler in existing application, so one should be added. Handler is short for "response handler". We have many types of responses:

  • Text response
  • Options response
  • Redirect response
  • Set state response
    etc.

For each response we may have response handler. Handler will accept Response in it's constructor arguments ({ response: ... }). Handle process can be triggered by call method.

We have multiple platforms (telegram, cli), and each platform can have it's own response handler. For example, text response handler will send a message in case of telegram platform, and print the message to console for cli platform.

When the message sent to telegram platform, the result in most cases will came from the user who is using Telegram application. In this case it's easy, and state machine will be able to handle it correctly.

But when the message is sent to cli platform, we usually expect some kind of user interaction (for text response there probably won't be any interaction, but for options response there should be interaction). So call method for cli platform will have the following signature:

call(onResult) { ... }

where onResult is optional callback, and can be executed when there is result (choice from the list of options). And can be skipped (for example, if it's text message only).

Folders should be organized the following way:

|- /response-handlers
|     |- /cli
|     |     |- text-handler.js
|     |- /telegram
|     |     |- text-handler.js (to be implemented later)
|     |- handler.js

In this story handler.js should be implemented as abstract class, and text-handler.js should be implemented as concrete implementation for cli.

Create ErrorResponse

ErrorResponse is type of Response with the type error and represents the error message. It contains only error message. Error response is not exception, is just a way to tell user that something goes wrong inside of menu action. For example, somehow invalid option was selected. Then error message is generated. It can be displayed to the user with error icon.

StateStorage -> Stateful mixin

Refactor StateStorage into Stateful mixin that can be applied to any class. Usage example:

class User extends Stateful(ValidatedUser) {
    constructor() {
        ....
        this.stateful = {
            table: 'users',
            key: new UserKey(platformType, platformId).toString(); // cli_1
        };
    }
}

or

class Command extends Stateful() {
    constructor() {
        ....
        this.stateful = {
            table: 'commands',
            key: new StateKey(platformType, platformId, guid).toString(); // cli_1_guid
        };      
    }
}

or

class Foo extends Stateful() {
    constructor() {
        ...
        this.stateful = {
            table: 'foos',
            key: '1'
        };
    }
}

Stateful Foo usage example:

const foo = new Foo();
foo.load().then((foo) => {
    foo.state.bar = 123; // set bar property of "state" object
    foo.save();
});

Stateful mixin implementation will look like:

let Stateful = (superclass) => class extends superclass {
    load() {
        // ...
    }
}

Add SelectLanguageResponse

SelectLanguageResponse is subclass of UserStateResponse that represents data transfer object that keeps selected language. Response handler (to be implemented later) will save language to user's state.

class SelectLanguageResponse extends UserStateResponse ...

Usage example:

return new SelectLanguageResponse('en');

Depends on #50

Add translations infrastructure

Project initially should have translation infrastructure, so everything can be translated into many languages. For particular translation item description should be provided. Description should have description of the context where the phrase is used. This information will be used by translators (real persons) to help them understand the context of particular phrase without digging into application.

Example:

phrase: "Welcome to our system! Type /help for the list of commands"
language: "en"
desc: "This is a banner. This banner is displayed every time to new users when they add the bot to their contacts and type /start".

Description exists in only one language - English.

Make User stateful

User should be stateful and this syntax should work:

  const user = await new User({ platformType: 'cli', platformId: '1' }).load();
  user.state.location = 'something';
  user.save();

Add .env for tests

There is .env-sample in project root directory that contains required fields for program to operate properly. Enginner supposed to copy .env-sample to .env after pulling the project from source code repository. .env is git-ignored, because the main idea is not to commit production values.

But there is need for .env file for tests. This .env file should be located in test directory, should not be git-ignored, and should have test parameters. Also, parameter FIREBASE_STATES_CONNSTR should be added to both test/.env and .env-sample - to be used later.

Create user

User entity should be created with the following properties:

  • platformType - type of the platform. Examples: cli for console application, telegram for Telegram, whatsapp for Whatsapp, etc. cli should be supported by default, others can be added now or later.
  • platformId - user identified for the platform. Telegram user, for instance, may have id set to random value like 123456.

So we'll have the set of properties that will represent the user, regardless of the platform. Moreover, we'll be able to connect two users from different platforms.

Basic validation should be implemented.

Refactor redirect response

Redirect response can be simplified, because ActionFactory can create actions only from existing map (routes). See here.

routes should exist in separate file. ActionFactory should import routes. Redirect response should validate in constructor that route actually exists.

Update .env-sample

.env-sample should be updated, because it contains no information about LOG_FILE. It should be set to cheaptaxi.log by default.

Add cli options response handler

Create CLI options response handler. From documentation:

Options response is response that contains rows of options. Each row is array. Each option in the row is object with label and value properties set.

Example:

const r = new OptionsResponse({
    rows: [
        [{ label: 'One', value: '1' },{ label: 'Two', value: '2' },{ label: 'Three', value: '3' }],
        [{ label: 'OK', value: 'ok' },{ label: 'Cancel', value: 'cancel' }]
    ]
});
// response above represents the following structure:
// row 1:   One  |   Two   | Three
// row 2:        OK   |   Cancel

In CLI all options should be flattened (only one row).

Inquirer.js is a good choice to display the list of options. See example

Only one option can be selected.

StateKey -> StatefulKey

StateKey was designed for StateStorage (Stateful now) and now should be slightly modified:

  • Rename to StatefulKey
  • guid should be optional

If guid is not specified, key should be generated the following way:

platformtype_platformid

For example:

cli_1

or

telegram_31337

StatefulKey can be used for actions (have guids), users (don't have guids), commands (may have guids).

Readme update

"connection string for Firebase for stateful objects." --> "Firebase connection string for stateful objects"

Improve logging system

Logging system should have LOG_FILE parameter in .env. Logging system should be configurable for tests. It should be silent by default with option to enable logging to console and/or file. Log filenames for test outputs can be hardcoded, but must be git-ignored.

It also may include dotenv configuration.

Simply bump version to 0.1.0

Version 1.0.0 is not recommended. Official SEMVER recommendation is:

The simplest thing to do is start your initial development release at 0.1.0 and then increment the minor version for each subsequent release.

It also needs to be updated to use esdoc tags like @since in the future.

Add state storage

StateStorage keeps state data, updates state when underlying storage updates. Objects in application can be stateful. StateStorage is basic structure that implements persistent state (won't go away on app restart).

Key for constructor below is already existing new StateKey(...).toString() - something that identifies particular command for particular user for particular platform.

StateStorage implementation:

constructor(key) - Constructor. key - state key, for example telegram_31337_4566bd48-d369-4594-aa1e-fb5dae1acf43.
load() - Loads state from the actual storage. Returns promise which is executed when changes were initially loaded.
save() - updates the storage with current state, returns promise. Will only overwrite the children enumerated in state.
setState({ ... }) - updates selected properties.
state - actual state.
dispose - unsubscribes itself from storage updates (off method for Firebase).

Usage example:

new StateStorage('telegram_31337_4566bd48-d369-4594-aa1e-fb5dae1acf43')
    .load()
    .then((storage) => {
        console.dir(storage.state); // => {}
        storage.setState({ a: 1, b: 2 });
        storage.save().then(() => {
            console.log('saved');
        })
    });

Create UserStateResponse

UserStateResponse is type of Response with the type user-state, and will be used to tell the handler to update (current) user's state. Constructor accepts hash of properties to be set.

For example, SelectLanguage action can return this response, and user.state.locale will be set to en. The code of SelectLanguage.execute method may look like:

// ...
return new UserStateResponse({ locale: 'en' });

However, the code above is not a good practice. UserStateResponse is the base class for all state operations. For example, the line above can be rewritten to:

return new SelectLanguageResponse('en');

And SelectLanguageResponse extends UserStateResponse:

class SelectLanguageResponse extends UserStateResponse ...

Create sample menu tree

Concept

Concept is to build menu tree similar to other bots like Botan. Example:

image

When Settings clicked:

image

Menu structure

Create sample menu tree with the following menu structure

0.1.0  -+- no_type -+- select_type
        |          +- catch_all
        |
        +- client -+- request taxi
        |          +- settings
        |          +- catch_all
        |
        +- driver -+- imhere
                   +- settings
                   +- catch_all

catch_all - command to catch all other requests.
imhere - menu item that drivers should pick every hour to update their current location
0.1.0 - menu version. Will be adjusted according to SEMVER.

Client menu location

Client menu location is array of menu ids:

['0.1.0', 'no_type', 'select_type']

Lines above are default menu location for the user. /start command will just execute command with id select_type.

Executing commands

Example of executing command with default location:

cmd = menu.handler;
cmd.execute();

QUESTIONABLE:

buildCommand constructs Command object. Constructor accepts two parameters:

  • context - see below
  • state - state of the object, fetched from the menu.

context has the following properties:

  • user - user object
  • i18n - object for translations, initialized with locale property in user object
  • request - request from the user. If it's empty, it means that menu item has been just picked and command should reply with what is expected.
  • response - callback that accepts two parameters: state and text which needs to be sent to the user. This callback updates state in the database, and when successful, sends response to the user. It can also update new menu location.

See also:

Refactor mix usage

mix is used in many places:

mix(Dummy)...
mix(Object)...

It should be replaced with mix(class {}), this syntax doesn't require class to be defined. And mix(Object) has side effects -- class functions don't work.

Cleanup text response constructor

TextResponse constructor contains validation (checking message parameter for null). It should be replaced with checkNotNull validating mixin.

Fix test subdirs

Subdirectories are not included by mistake. So files from ./test/ are included, but from ./test/validations are not. It should be fixed, plus failing tests (if any) should be fixed.

Add menu action factory method

ActionFactory.fromLocation (or Action.fromLocation) factory method should be introduced. It will accept user.state.location parameter.

location is pointing to the relative path of .js file inside of src/menu folder. Factory method should import and create instance of class from result location.

For example, if location is select-user-type.js, file from ./src/menu/select-user-type.js should be loaded, and default class from this file should be instantiated. It's enough for now, later other functionality can be added - when Action will be introduced.

Also, empty/undefined location should be resolved to default menu item, which is now select-language.js (it's okay to have stub for now).

Add StateKey

StateKey is object that represents the key for State object. There can be unlimited number of states (each state object is just a hash). Key is unique identifier used to to retrieve particular state (hash) from the storage.

It should have constructor that accepts parameters:

  • platformType - type of the platform
  • platformId - unique user identifier for this particular platform
  • guid - guid of the object that is using state

Idea is that state can be mixed into any object (as state property). And each menu action for particular user should have its own state. Menu actions can't have the same state for all users (that's why we can't have one guid parameter). State is not shared between users and between commands.

StateKey must have toString method that returns string representation of the key in the following format:

platform_userid_guid

Example:

telegram_31337_4566bd48-d369-4594-aa1e-fb5dae1acf43

Key should be lowercased.

Test helpers for checkNotNull mixin

checkNotNull validating mixin works, but testing classes containing this mixin can be simplified. Helper method should be introduces. Syntax example:

checkNotNullTest(['foo', 'bar'], () => {
    return MyClass;
});

or for single property:

checkNotNullTest('foo', () => {
    return MyClass;
});

return MyClass intentionally has no new keyword, because internally, inside of checkNotNullTest, new object (that needs to be tested) will be constucted with new keyword and hash of parameters:

{ foo: true, bar: true }

or

{ foo: true }

Create OptionsResponse

OptionsResponse is type of Response with the type options. Constructor accepts array of rows, where each row is array of option objects. This allows to display options to the user in a grid format like:

row 1:   One  |   Two   | Three
row 2:        OK   |   Cancel

Implementation of the above:

[
  [{ label: 'One', value: '1' }, { label: 'Two', value: '2' }, { label: 'Three', value: '3' }],
  [{ label: 'OK', value: 'ok' }, { label: 'Cancel', value: 'cancel' }]
]

option object:

{ label: 'One', value: '1' }

where label is label to display, and value is value that will be passed to execute method of Action. In the example above OptionsResponse can generate 5 values:

  • 1
  • 2
  • 3
  • ok
  • cancel

Later Action.execute must handle these five values.

Note: representation of option object for Telegram is slightly different: { text: '...', callback_data: '...' } (see here). Later Telegram decorator can be applied.

Add generic not null validating mixin

Not null validating mixing is generic validating mixin that can be used anywhere across the program. The main idea is to avoid creating multiple validation mixins when only null check is required.

For example:

    if (!opts.message) {
      throw new ArgumentError('message parameter not specified');
    }

^^^ actual code from text-response.js.

Moving this functionality to validated-text-response.js is redundant, because it just validates one property. To speed up development new mixin should be introduced.

Here is how it can work:

export default class TextResponse extends mix(Response).with(CheckNotNull('message'))

CheckNotNull can be imported standard way:

import CheckNotNull from './check-not-null-validation.js';

The code above will mix TextResponse with CheckNotNull validating mixin instead of creating new class. This validating mixin can be reused in other places.

CheckNotNull must accept parameters of two types: String and Array. If parameter is the type of String, it will check only one property. If Array - the list of properties.

Add logging system

Logging system should output logs to console and to log file. Date and time for each log line should be specified along with log level, e.g. [2016-03-29 10:45:59] [info] Log line goes here.

Refactor check not null mixin

checkNotNull validating mixin should be refactored so it will work without parameters. If parameters are not specified, mixin should just check if object is provided.

Add redirect response

Redirect response is a type of response (along with text, menu, composite, etc.) which is used to redirect users to certain menu location. It accepts only one parameter - path, which can be absolute (starts with /) or relative (starts with .). It should always ends with .js. Redirect response should throw exceptions if it's malformed. Redirect response doesn't verify the presence of the actual file on the file system, because path can be relative. And redirect response knows nothing about menu structure and handlers location on file system.

Add composite response

While atomic responses are useful, it's often required to return composite response from the action. For example, on post action can return three responses:

    return new CompositeResponse()
      .add(new TextResponse(`You selected ${value}`))
      .add(new SelectLocaleResponse(value))
      .add(new RedirectResponse(...)); // TODO

First response will force handler to send the text to the user. Second is used to update state. And third is used to redirect the user to new url.

CompositeResponse must be implemented. It should have add method and responses property, representing array of responses.

Improve OptionsResponse

OptionsResponse must be improved. Now it's quite hard to read when you want to initialize it:

const response = new OptionsResponse({
    rows: [
        [{ label: 'One', value: '1' },{ label: 'Two', value: '2' },{ label: 'Three', value: '3' }],
        [{ label: 'OK', value: 'ok' },{ label: 'Cancel', value: 'cancel' }]
    ]
});

It can be simplified by introducing two methods row() and add(...). For example:

const response = new OptionsResponse()
   .row().add('One', 1).add('Two', 2).add('Three', 3)
   .row().add('OK', 'ok').add('Cancel', 'cancel');

or

const response = new OptionsResponse().row();
response = response.add('One', 1);
response = response.add('Two', 2);

Note: there is no new keyword, because row and add return new objects.

Add esdoc

Esdoc should be integrated into the project. It also includes adding simple comments, updating .gitignore for documentation directory, updating README.md to mention doc generating process.

Also, DEVELOPMENT.md needs to be added. Format of esdoc comments should be explained along with development tools configuration.

Depends on:

#14

Create initial infrastructure

Initial infrastructure should have:

  • ES6 support
  • ava - test runner
  • dotenv - environment variables loader with .env-sample file. .env should be added to .gitignore
  • Async support through babel
  • npm start and npm test commands to run and test application
  • Updated README file on how to configure application (npm i, copy .env-sample to .env, npm start), link to telegram bot in README file.

Refactor validation for stateful key

validated-stateful-key.js has ValidatedStatefulKey, but during refactoring it turned out that this name doesn't reflect the function of this type anymore. So it can be refactored to something more generic and reused later (potential reuse is user.js).

Suggested name checkPlatformType.

Fix npm esdoc script

esdoc script should be fixed the way it will NOT open browser with documentation after npm run esdoc.

Refactor response and it's validation

Response has ValidatedResponse mixin. It can be refactored and cleaned up. It requires parameter type and checkNotNull mixin can be used instead of code in existing validating mixin.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.