GithubHelp home page GithubHelp logo

boshurik / telegrambotbundle Goto Github PK

View Code? Open in Web Editor NEW
68.0 3.0 23.0 195 KB

Symfony Telegram Bot Bundle

License: MIT License

PHP 100.00%
php symfony symfony-bundle telegram-bot-bundle telegram-bot telegram

telegrambotbundle's Introduction

TelegramBotBundle

Telegram bot bundle on top of telegram-bot/api library

Examples

See example project

Installation

Composer

$ composer require boshurik/telegram-bot-bundle

If you are using symfony/flex all you need is to set TELEGRAM_BOT_TOKEN environment variable

Register the bundle

<?php
// app/AppKernel.php

public function registerBundles()
{
    $bundles = array(
        // ...
        new BoShurik\TelegramBotBundle\BoShurikTelegramBotBundle,
    );
    // ...
}

Add routing for webhook

BoShurikTelegramBotBundle:
    resource: "@BoShurikTelegramBotBundle/Resources/config/routing.php"
    prefix: /_telegram/%telegram_bot_route_secret%

or for multiple bots:

BoShurikTelegramBotBundle:
    resource: "@BoShurikTelegramBotBundle/Resources/config/routing.php"
    prefix: /_telegram/{bot}/%telegram_bot_route_secret%

Configuration

boshurik_telegram_bot:
    api:
        token: "%telegram_bot_api_token%"
        proxy: "socks5://127.0.0.1:8888"

or for multiple bots:

boshurik_telegram_bot:
    api:
        default_bot: first
        bots:
            first: "%first_telegram_bot_api_token%"
            second: "%second_telegram_bot_api_token%"
        proxy: "socks5://127.0.0.1:8888"

Usage

API

To get default bot api:

use TelegramBot\Api\BotApi;
public function __construct(private BotApi $api)

For multiple bots:

use BoShurik\TelegramBotBundle\Telegram\BotLocator;
use TelegramBot\Api\BotApi;

public function foo(BotLocator $botLocator)
{
    /** @var BotApi $api */
    $api = $botLocator->get('first');
}

or use argument with type TelegramBot\Api\BotApi and name pattern /\${name}(Bot|BotApi|Api)?$/

use TelegramBot\Api\BotApi;
public function __construct(private BotApi $firstBotApi)

For more info see Usage section in telegram-bot/api library

Getting updates

bin/console telegram:updates
bin/console telegram:updates first

For more information see official documentation

Webhook

Set
bin/console telegram:webhook:set [url-or-hostname] [<path-to-certificate>]
bin/console telegram:webhook:set [url-or-hostname] [<path-to-certificate>] --bot first

If url-or-hostname is not set command will generate url based on request context

Unset
bin/console telegram:webhook:unset
bin/console telegram:webhook:unset first

For more information see official documentation

Async command processing

To improve performance, you can leverage Messenger to process webhooks later via a Messenger transport.

composer req symfony/messenger
# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            async: "%env(MESSENGER_TRANSPORT_DSN)%"

        routing:
            'BoShurik\TelegramBotBundle\Messenger\TelegramMessage': async

Adding commands

Commands must implement \BoShurik\TelegramBotBundle\Telegram\Command\CommandInterface

There is \BoShurik\TelegramBotBundle\Telegram\Command\AbstractCommand you can start with

To register command: add tag boshurik_telegram_bot.command to service definition

app.telegram.command:
    class: AppBundle\Telegram\Command\SomeCommand
    tags:
        - { name: boshurik_telegram_bot.command }

If you use autoconfigure tag will be added automatically

For application with multiple bots you need to pass bot id:

app.telegram.command:
    class: AppBundle\Telegram\Command\SomeCommand
    tags:
        - { name: boshurik_telegram_bot.command, bot: first }

If you need to use same command for multiple bots you must add multiple tags for each bot:

app.telegram.command:
    class: AppBundle\Telegram\Command\SomeCommand
    tags:
        - { name: boshurik_telegram_bot.command, bot: first }
        - { name: boshurik_telegram_bot.command, bot: second }

There is predefined \BoShurik\TelegramBotBundle\Telegram\Command\HelpCommand. It displays commands which additionally implement \BoShurik\TelegramBotBundle\Telegram\Command\PublicCommandInterface

You need to register it:

app.telegram.command.help:
    class: BoShurik\TelegramBotBundle\Telegram\Command\HelpCommand
    arguments:
        - '@boshurik_telegram_bot.command.registry.default'
    tags:
        - { name: boshurik_telegram_bot.command }

or for multiple bots:

app.telegram.command.help:
    class: BoShurik\TelegramBotBundle\Telegram\Command\HelpCommand
    arguments:
        - '@boshurik_telegram_bot.command.registry.first'
    tags:
        - { name: boshurik_telegram_bot.command, bot: first }

Events

For more complex application (e.g. conversations) you can listen for BoShurik\TelegramBotBundle\Event\UpdateEvent event

/**
 * @param UpdateEvent $event
 */
public function onUpdate(UpdateEvent $event)
{
    $update = $event->getUpdate();
    $message = $update->getMessage();
}

Login with Telegram

This bundle supports login through Telegram Api

If you want to allow your Bot's users to login without requiring them to register again follow these instructions.

telegrambotbundle's People

Contributors

akuzia avatar bigfoot90 avatar boshurik avatar gassan avatar los-romka avatar martcor avatar skukunin 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

Watchers

 avatar  avatar  avatar

telegrambotbundle's Issues

[Feature] Login through Telegram

I have created a web site for my Bot, and want to permit users to login using their Telegram account.
I've created a custom Authenticator following the instructions on this page.

But would be awesome is this bundle could provide a solution out of the box.

Edit: Created a gist with my current TelegramAuthenticator implementation
Tested on Symfony 5.0
and shoud work on 4.4 also, but I can't test it now.

webhook с https не работает

у меня установлена ssl сертификат на сервере, через консоль установил вебхук на адрес https://api.mysite.com/_telegram/secret-token и когда отправляю запрос на юрл вебхука почему-то запрос приходит по http и получаю 404. можете помочь?

Only one commands works

When I was practising writing bot telegrams, initially there were several commands and they worked as planned. After adding a command with steps, a problem arose. For any message, only the last command with steps is triggered.. Implementation as in the bot example
If I delete the command with steps, everything works fine.
Please tell me what could be the problem?
Thanks for the help.

Cannot edit MessageMedia

Hi everyone!

I'm trying to edit message photo. I have written a method editMessageMedia (https://core.telegram.org/bots/api#editmessagemedia) in vendor/telegram-bot/src/BotApi.php:

class BotApi
{
    ...
    public function editMessageMedia(
        $chatId,
        $messageId,
        InputMedia $media,
        $inlineMessageId = null,
        $replyMarkup = null
    ) {
        return Message::fromResponse($this->call('editMessageMedia'), [
            'chat_id' => $chatId,
            'message_id' => $messageId,
            'inline_message_id' => $inlineMessageId,
            'media' => $media->toJson(),
            'reply_markup' => is_null($replyMarkup) ? $replyMarkup : $replyMarkup->toJson(),
        ]);
    }
}

And I use it so:

class MyCommand extends AbstractCommand implements PublicCommandInterface {
    ...
    
    public function execute(BotApi $api, Update $update)
    {
        $data['photo'] = 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png';

        $photo = new InputMedia();
        $photo->setType('photo');
        $photo->setMedia($data['photo']);

        $api->editMessageMedia(
            $data['chatId'],
            $data['messageId'],
            $photo,
            $data['inlineMessageId'] ?? null,
            $data['replyMarkup'] ?? null
        );
    }
}

And I'm getting an error:
Bad Request: can't parse InputMedia: Media is not specified
I have tried everything... Hoe can I fix this problem and edit photo in message?

Thanks in advance.

Allow Symfony 7

Can you please make a release that allows Symfony 7 dependencies?

Error on Symfony 4

I'm trying this bundle, but when I want to initialize the $api like you do in your docs I get this error:
"The "bo_shurik_telegram_bot.api" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.".

Any idea what's wrong?

This is what I try:

/** @var TelegramBot\Api\BotApi $api */
$api = $this->container->get('bo_shurik_telegram_bot.api');

Message can be NULL and it crashes in AbstractCommand::isApplicable()

Somehow, I don't know why (I've just edited a previous message in Telegram window, not sure why), I've got an update like this:

object(TelegramBot\Api\Types\Update)#583 (4) {
  ["updateId":protected]=>
  int(74300160)
  ["message":protected]=>
  NULL
  ["inlineQuery":protected]=>
  NULL
  ["chosenInlineResult":protected]=>
  NULL
}

it has no message attached. This leads to crash on telegram:updates command:

  [Symfony\Component\Debug\Exception\FatalThrowableError]
  Type error: Argument 1 passed to BoShurik\TelegramBotBundle\Telegram\Command\AbstractCommand::isApplicable() must be an instance of TelegramBot\Api\Types\Message, null gi
  ven, called in /www/pokeroff.dev/vendor/boshurik/telegram-bot-bundle/EventListener/CommandListener.php on line 40

it appears from PHP docs that "To specify a type declaration, the type name should be added before the parameter name. The declaration can be made to accept NULL values if the default value of the parameter is set to NULL.", then if I update the declaration of isApplicable from:

public function isApplicable(Message $message)

to

public function isApplicable(Message $message = NULL)

the issue is gone.

Webhook UnsetCommand

On Symfony 4.4 returns
[info] User Deprecated: Return value of "BoShurik\TelegramBotBundle\Command\Webhook\UnsetCommand::execute()" should always be of the type int since Symfony 4.4, NULL returned.

Inline Bot mention handling

Hey! Your work looks really amazing and very helpful, thank you!

Have a question about the development of the inline bot. Is it possible to handle a simple mention of the bot with your stuff? After tagging the inline bot, @myBotName, I need to send messages. But unfortunately didn't find how to implement it.

HelpCommand can be more useful if description can be given in configuration

Like this:

    app.telegram.command.help:
        class: BoShurik\TelegramBotBundle\Telegram\Command\HelpCommand
        arguments:
            - "@bo_shurik_telegram_bot.command_pool"
            - "Other commands are: /blabla and /bubu"
        tags:
            - { name: bo_shurik_telegram_bot.command }

this requires to add one additional parameter to the constructor. Otherwise I'm forced to subclass HelpCommand and override the getDescription() method, while /help is very common command.

[Deprecation] Referencing controllers with a single colon is deprecated

Symfony 5.1.7 on PHP 7.4 report: User Deprecated: Since symfony/http-kernel 5.1: Referencing controllers with a single colon is deprecated. Use "BoShurik\TelegramBotBundle\Controller\WebhookController::indexAction" instead.

In prod mode it is simply given 404, while in dev it works cheerfully.

To solve, just edit: src/Resources/config/routing.yml line 4:

_controller: BoShurik\TelegramBotBundle\Controller\WebhookController:indexAction
to
_controller: BoShurik\TelegramBotBundle\Controller\WebhookController::indexAction

Thanks.

Executing Command

Hi, everyone!

I want to execute a command from another command with sendMessage method. /start calls /clients command and they have Inline Keyboard buttons.

It works well, but now StartCommand calls ClientsCommands via editMessageText method...

I mean:
StartCommand -> ClientsCommand (sendMessage)
ClientsCommands -> ClientsCommand (editMessageText, normal use)

How can I do this?

Thanks in advance.

StartCommand

...

public function execute(BotApi $api, Update $update)
    {
        $index = isset($update) ? $this->getIndex($update) : 'start';

        $messageId = $chatId = null;

        if ($update->getCallbackQuery()) {
            $chat = $update->getCallbackQuery()->getMessage()->getChat();
            $messageId = $update->getCallbackQuery()->getMessage()->getMessageId();
        } else {
            $chat = $update->getMessage()->getChat();
        }

        $this->showSection($api, $index, $chat->getId(), $messageId);
    }

    /**
     * @param Update $update
     * @return bool
     */
    public function isApplicable(Update $update)
    {
        if (parent::isApplicable($update)) {
            return true;
        }
        return $this->getIndex($update) !== null;
    }

    /**
     * @param Update $update
     * @return mixed|null
     */
    private function getIndex(Update $update)
    {
        if ($update->getMessage() && preg_match(self::REGEX_INDEX, $update->getMessage()->getText(), $matches)) {
            return $matches[1];
        }
        if ($update->getCallbackQuery() && preg_match(self::REGEX_INDEX, $update->getCallbackQuery()->getData(), $matches)) {
            return $matches[1];
        }
        return null;
    }

    /**
     * @param BotApi $api
     * @param $index
     * @param $chatId
     * @param null $messageId
     * @throws \TelegramBot\Api\Exception
     * @throws \TelegramBot\Api\InvalidArgumentException
     */
    private function showSection(BotApi $api, $index, $chatId, $messageId = null) {
        $replyMarkup = new InlineKeyboardMarkup([
            [['text' => 'Über uns', 'callback_data' => '/start_about']],
            [['text' => 'Kunden', 'callback_data' => '/clients_0']]
        ]);

        switch ($index) {
            case 'about':
                $text = 'Text from *About* section';
                break;

            case 'clients':
                $text = 'Text from *Clients* section';
                break;

            default:
                $text = "Hello :)" .
                    "❓ How can I help you?";
        }

        if ($messageId) {
            $api->editMessageText(
                $chatId,
                $messageId,
                $text,
                'markdown',
                false,
                $replyMarkup
            );
        } else {
            $api->sendMessage(
                $chatId,
                $text,
                'markdown',
                false,
                null,
                $replyMarkup
            );
        }
    }

ClientsCommand

... 

public function execute(BotApi $api, Update $update)
    {
        $clients = $this->repository->findAll();
        $index = (int)$this->getIndex($update);
        $index = isset($clients[$index]) ? $index : 0;

        $messageId = $chatId = null;

        if ($update->getCallbackQuery()) {
            $chat = $update->getCallbackQuery()->getMessage()->getChat();
            $messageId = $update->getCallbackQuery()->getMessage()->getMessageId();
        } else {
            $chat = $update->getMessage()->getChat();
        }

        $this->paginateClients($api, $index, $chat->getId(), $messageId);
    }

    /**
     * @param Update $update
     * @return bool
     */
    public function isApplicable(Update $update)
    {
        if (parent::isApplicable($update)) {
            return true;
        }
        return $this->getIndex($update) !== null;
    }

    /**
     * @param Update $update
     * @return mixed|null
     */
    private function getIndex(Update $update)
    {
        if ($update->getMessage() && preg_match(self::REGEX_INDEX, $update->getMessage()->getText(), $matches)) {
            return $matches[1];
        }
        if ($update->getCallbackQuery() && preg_match(self::REGEX_INDEX, $update->getCallbackQuery()->getData(), $matches)) {
            return $matches[1];
        }

        return null;
    }

    /**
     * @param BotApi $api
     * @param $index
     * @param $chatId
     * @param null $messageId
     * @throws \TelegramBot\Api\Exception
     * @throws \TelegramBot\Api\InvalidArgumentException
     */
    private function paginateClients(BotApi $api, $index, $chatId, $messageId = null) {
        $count = count($this->repository->findAll());
        $client = $this->repository->findById($index);

        if ($index === 'execute') {
            $index = 0;
        }

        $prev = $next = null;
        if ($index - 1 >= 0) {
            $prev = $index - 1;
        }
        if ($index + 1 < $count) {
            $next = $index + 1;
        }
        $buttons = [];

        if ($prev !== null) {
            $buttons[] = ['text' => '⬅️', 'callback_data' => '/clients_' . $prev];
        }

        $buttons[] = ['text' => '🌐 Zur Website', 'url' => $client['link']];

        if ($next !== null) {
            $buttons[] = ['text' => '➡️', 'callback_data' => '/clients_' . $next];
        }

        $text = "▫️ *{$client['title']}*\n\n" .
            "📋 _{$client['description']}_";

        if ($messageId) {
            $api->editMessageText(
                $chatId,
                $messageId,
                $text,
                'markdown',
                false,
                new InlineKeyboardMarkup([$buttons])
            );
        } else {
            $api->sendMessage(
                $chatId,
                $text,
                'markdown',
                false,
                null,
                new InlineKeyboardMarkup([$buttons])
            );
        }
    }

Example of adding a command

Hi,

Is there any chance you could show how to implement a custom command. I have tried to follow the documentation but as soon as I start to place command files in my own bundle and adds them to services.yml applications just dies.

Adding the provided /help-command works though. Now I just want to find a way to add a let's say /test-command that replies with "Hello world" I have the file but I can't manage to figure out how to reference in right without errors.

If you could give an example of this it would be very helpful.

How to disable built in `/help` command?

Hello.

I'm making a bot that should not have any commands (except /start but it is not exactly). It will be operated by buttons and keyboard.
How can I disable built in /help command? It implements the PublicCommandInterface and the CommandInterface so with autoconfigure enabled removing a boshurik_telegram_bot.command tag from config does't make any effect. /help is still there.
I'd like to keep autoconfigure enabled.

Crash in console if no commands configured

Thank you for such a useful bundle. I'm just trying to setup it for my project. I have no commands configured yet, but running from telegram:updates crashes on foreach($this->commandPool->getCommands()) loop where getCommands() returns NULL like that:

yuri@zerg tbot (master ✭ ✗ ◼) » php app/console -v telegram:updates

  [Symfony\Component\Debug\Exception\ContextErrorException]
  Warning: Invalid argument supplied for foreach()

Exception trace:
 () at /www/tbot/vendor/boshurik/telegram-bot-bundle/EventListener/CommandListener.php:39
 Symfony\Component\Debug\ErrorHandler->handleError() at /www/tbot/vendor/boshurik/telegram-bot-bundle/EventListener/CommandListener.php:39
 BoShurik\TelegramBotBundle\EventListener\CommandListener->onUpdate()...

Please make necessary check.

And also it is unclear from the documentation what does "bin/console telegram:webhook" do. Please give some description for beginners. Thank you.

Created a telegram command which depends on the service

I am trying to create a command which depends on the service and the service depends on redis.
Autowire is specified in the `config/services.yaml ' settings

services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true
        autoconfigure: true
        bind:
          Redis $redisDefault: '@snc_redis.default'

The service code is as follows

class VerificationCode
{
    /**
     * @var \Redis
     */
    protected $redis;

    /**
     * @param \Redis $redis
     */
    protected function __constructor(\Redis $redisDefault)
    {
        $this->redis = $redisDefault;
    }
}

The command code for telegram is as follows

class VerificationCodeCommand extends AbstractCommand implements PublicCommandInterface
{
    protected $verificationCode;

    /**
     * VerificationCodeCommand constructor.
     */
    public function __construct(VerificationCode $verificationCode)
    {
        $this->verificationCode = $verificationCode;
    }
}

The cache that symfony generates is not correct, and no errors are thrown during the call, but the VerificationCode constructor does not work.

Symfony cache code:

namespace ContainerDqZ4H22;

use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;

/**
 * @internal This class has been auto-generated by the Symfony Dependency Injection Component.
 */
class getBoshurikTelegramBot_Command_ListenerService extends App_KernelDevDebugContainer
{
    /**
     * Gets the private 'boshurik_telegram_bot.command.listener' shared service.
     *
     * @return \BoShurik\TelegramBotBundle\EventListener\CommandListener
     */
    public static function do($container, $lazyLoad = true)
    {
        include_once \dirname(__DIR__, 4).'/vendor/boshurik/telegram-bot-bundle/src/EventListener/CommandListener.php';
        include_once \dirname(__DIR__, 4).'/vendor/boshurik/telegram-bot-bundle/src/Telegram/Command/CommandRegistry.php';
        include_once \dirname(__DIR__, 4).'/vendor/boshurik/telegram-bot-bundle/src/Telegram/Command/CommandInterface.php';
        include_once \dirname(__DIR__, 4).'/vendor/boshurik/telegram-bot-bundle/src/Telegram/Command/AbstractCommand.php';
        include_once \dirname(__DIR__, 4).'/vendor/boshurik/telegram-bot-bundle/src/Telegram/Command/PublicCommandInterface.php';
        include_once \dirname(__DIR__, 4).'/src/Model/Telegram/Commands/VerificationCodeCommand.php';
        include_once \dirname(__DIR__, 4).'/src/Model/Telegram/Service/VerificationCode.php';

        $a = new \BoShurik\TelegramBotBundle\Telegram\Command\CommandRegistry();
        $a->addCommand(new \App\Model\Telegram\Commands\VerificationCodeCommand(($container->privates['App\\Model\\Telegram\\Service\\VerificationCode'] ?? ($container->privates['App\\Model\\Telegram\\Service\\VerificationCode'] = new \App\Model\Telegram\Service\VerificationCode()))));

        return $container->privates['boshurik_telegram_bot.command.listener'] = new \BoShurik\TelegramBotBundle\EventListener\CommandListener(($container->privates['TelegramBot\\Api\\BotApi'] ?? $container->load('getBotApiService')), $a);
    }
}

Tell me plz what could be the problem?
Thnx

Is Symfony 4 supported?

Hello, thanks for the bundle! I encountered a problem while installing your bundle for the symfony/skeleton project: https://github.com/symfony/skeleton

Steps to reproduce:

  1. composer create-project symfony/skeleton test
  2. cd test
  3. composer require boshurik/telegram-bot-bundle

Here is the output:

./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 2 installs, 0 updates, 0 removals
  - Installing telegram-bot/api (v2.3.7): Loading from cache
  - Installing boshurik/telegram-bot-bundle (2.3.1): Loading from cache
Writing lock file
Generating autoload files
Symfony operations: 1 recipe (ad09ff1cd63feb8c60fc961551359110)
  - Configuring boshurik/telegram-bot-bundle (>=2.3.1): From auto-generated recipe
Executing script cache:clear [KO]
 [KO]
Script cache:clear returned with error code 1
!!  
!!  In ArrayNode.php line 238:
!!                                                                               
!!    The child node "name" at path "boshurik_telegram_bot" must be configured.  
!!                                                                               
!!  
!!  

Can you explain please how to configure your bundle for Symfony 4?

Symfony 6.3 warning for future deprecations

Upgrading to Symfony 6.3 results in to deprecation warnings

Method "Symfony\Component\DependencyInjection\Extension\ExtensionInterface::load()" might add "void" as a native return type declaration in the future. Do the same in implementation "BoShurik\TelegramBotBundle\DependencyInjection\BoShurikTelegramBotExtension" now to avoid errors or add an explicit @return annotation to suppress this message.

Method "Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface::process()" might add "void" as a native return type declaration in the future. Do the same in implementation "BoShurik\TelegramBotBundle\DependencyInjection\Compiler\CommandCompilerPass" now to avoid errors or add an explicit @return annotation to suppress this message.

InlineKeyboard example

Hi everyone!

How can I build a command with inline keyboard buttons and callback handlers? I think that the need to be register, but how?

Thanks in advance

Add support .php configs

Symfony поддерживает конфиги вида

<?php

declare(strict_types=1);

use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return static function (RoutingConfigurator $routingConfigurator): void {
    $routingConfigurator->import('@BoShurikTelegramBotBundle/Resources/config/routing.ym')
        ->prefix("/_telegram/%telegram_route_secret%");
};

а в коде захардкожен yaml

Multiple bots feature is broken

I decided to upgrade from the fork to the updated version but ran into the fact that I can't just assign the bots directive and list my bots, I'm locked out on the old version

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.