Git Product home page Git Product logo

cardinal's Introduction

Cardinal

Meet Cardinal.

Build Status Coverage Status

Cardinal is a Python Twisted IRC bot with a focus on ease of development. It features reloadable asynchronous plugins, Python decorators for commands and IRC events, simple persistent JSON data storage, and a well-documented API.

You can join #cardinal on the DarkScience IRC network for questions or support. (irc.darkscience.net/+6697 โ€” SSL required)

What can Cardinal do?

Anything, if you're creative! Cardinal does come with some plugins to get you started...

  • Fetching URL titles
  • Wolfram Alpha calculations
  • Wikipedia definitions
  • Urban Dictionary definitions
  • Movie and TV show lookups
  • Weather reports
  • Reminders
  • Google searches
  • Now playing w/ Last.fm
  • Stock ticker
  • sed-like substitutions
  • ... and more!

But the best part of Cardinal is how easy it is to add more!

Basic Usage

Configuration

  1. Copy the config/config.example.json file to config/config.json (you can use another filename as well, such as config.freenode.json if you plan to run Cardinal on multiple networks).

  2. Copy plugins/admin/config.example.json to plugins/admin/config.json and add your nick and vhost in order to take advantage of admin-only commands (such as reloading plugins, telling Cardinal to join a channel, or blacklisting plugins within a channel).

Running

Cardinal is run via Docker. To get started, install Docker and docker-compose.

If your config file is named something other than config/config.json, you will need to create a docker-compose.override.yml file like so:

version: "2.1"
services:
    cardinal:
        command: config/config_file_name.json

To start Cardinal, run docker-compose up -d. To restart Cardinal, run docker-compose restart. To stop Cardinal, run docker-compose down.

Writing Plugins

Cardinal was designed with ease of development in mind.

from cardinal.decorators import command, help

class HelloWorldPlugin:
    @command(['hello', 'hi'])
    @help("Responds to the user with a greeting.")
    @help("Syntax: .hello")
    def hello(self, cardinal, user, channel, msg):
        nick, ident, vhost = user
        cardinal.sendMsg(channel, "Hello {}!".format(nick))

entrypoint = HelloWorldPlugin

Cardinal also offers a lightweight database API. Visit the wiki for detailed information.

Contributing

Cardinal is a public, open-source project, licensed under the MIT License. Anyone may contribute.

When submitting a pull request, you may add your name to the CONTRIBUTORS file.

cardinal's People

Contributors

biohzn avatar dependabot[bot] avatar dkim286 avatar flarf avatar foreverendeavor avatar helixspiral avatar johnmaguire avatar jsmailes avatar mvineetmenon avatar obviyus avatar someguy123 avatar target111 avatar tskuse avatar tsudoko 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

cardinal's Issues

Bot seems to hang on disconnect

Not sure if this happens 100% of times, but if the connection to the IRC network is killed, Cardinal seems to hang, rather than reconnecting in 15 seconds as it should. C-c also will not kill it, and it has to be killed with a "kill -9" command.

Allow UTF-8 characters in URL titles

Probably the same fix as 8141a89.

2015-02-27 15:00:47,614 - cardinal.plugins - INFO - Calling callbacks for event: irc.privmsg
Unhandled Error
Traceback (most recent call last):
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/policies.py", line 120, in dataReceived
    self.wrappedProtocol.dataReceived(data)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2430, in dataReceived
    basic.LineReceiver.dataReceived(self, data.replace('\r', ''))
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/basic.py", line 571, in dataReceived
    why = self.lineReceived(line)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2438, in lineReceived
    self.handleCommand(command, prefix, params)
--- <exception caught here> ---
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2482, in handleCommand
    method(prefix, params)
  File "/home/john/files/Cardinal-DH/cardinal/bot.py", line 139, in irc_PRIVMSG
    self.plugin_manager.call_command(user, channel, message)
  File "/home/john/files/Cardinal-DH/cardinal/plugins.py", line 611, in call_command
    command(self.cardinal, user, channel, message)
  File "/home/john/files/Cardinal-DH/plugins/urls/plugin.py", line 91, in get_title
    title = str(h.unescape(title))
exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\xb7' in position 12: ordinal not in range(128)

update plugin system to add new functionality

Here's how I'd like the plugin system to work:

Each plugin is loaded up and its constructor is called. The constructor will register commands based on what type of action is called (for example an ACTION, PRIVMSG, NICK, etc.), a regex or a command (with the command symbol prior to it) and a callback. On each action received by the bot, it will go through the list of callbacks, and hit any that are associated.

Plugins will have three possible components: plugin, which is necessary, api, which exposes some features to other plugins through a cardinal.api.<plugin_name> object, and a config which is where configuration data goes. All other plugins will have access to the APIs of currently enabled plugins and can even depend on other plugins in order to work.

Plugins will be also able to register with the bot hooks for certain functionality. For example, the urls plugin, which looks up URLs and returns the title of the page would allow other plugins to hook in, and override the default functionality.

For example, in the constructor, the urls plugin would call something like cardinal.register_hook('urls'). This would let Cardinal know that there is a hook other plugins can register to named urls. From the constructors of other plugins, you could call cardinal.register_hook_callback('urls', '/http:\/\/youtube.com\/watch?v=(.+)/', self.urls_callback). When the urls plugin calls cardinal.handle_callbacks('urls', url), Cardinal will check if any of the callbacks have regexes matching the url variable and if so will return true, letting the urls plugin know that some other functionality was called (allowing urls to no-op). If none were found matching, it will return false.

Lastly, there should be a plugin providing configuration support. This would allow plugins to have some on-the-fly configuration (such as set urls.detection off to turn detection of URLs off.)

Move plugin interface outside of CardinalBot

The goal is to move the interface that plugins are provided (e.g. through the cardinal param) outside of the CardinalBot class. Separating this out from the protocol-specific logic will make the ability to write different protocol drivers simpler.

Snapchat plugin

It would be cool to make a plugin that can accept snaps and send them to a configured IRC channel upon receiving them.

Plugin system needs a way to remove events / callbacks

Currently, if a plugin that defined an event is reloaded, it won't be able to re-define its event, since the EventManager will believe it still exists. Similarly, if an event that has registered a callback is reloaded, when EventManager tries to call it, Python will cry.

This blocks milestone Crimson (version 2.0).

No module named words.protocol

I executed $python cardinal.py
Its giving me

from twisted.words.protocols import irc
ImportError: No module named words.protocols

Time plugin (timezones)

The point of the module would be to allow the user to check what's the current time in some timezone. For example, if one would enter the following:

.time +1

it would return the current time of the GMT+1 timezone.

.time

without any arguments defaults to GMT +/- 0

.time -5

would return the time of GMT-5.

Unhandled error for ".note <nonexistent note>"

2015-04-27 00:26:25,770 - cardinal.plugins - INFO - Calling callbacks for event: irc.privmsg
Unhandled Error
Traceback (most recent call last):
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/policies.py", line 120, in dataReceived
    self.wrappedProtocol.dataReceived(data)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2430, in dataReceived
    basic.LineReceiver.dataReceived(self, data.replace('\r', ''))
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/basic.py", line 571, in dataReceived
    why = self.lineReceived(line)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2438, in lineReceived
    self.handleCommand(command, prefix, params)
--- <exception caught here> ---
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2482, in handleCommand
    method(prefix, params)
  File "/home/john/files/Cardinal-DH/cardinal/bot.py", line 144, in irc_PRIVMSG
    self.plugin_manager.call_command(user, channel, message)
  File "/home/john/files/Cardinal-DH/cardinal/plugins.py", line 630, in call_command
    command(self.cardinal, user, channel, message)
  File "/home/john/files/Cardinal-DH/plugins/notes/plugin.py", line 118, in get_note
    content = self._get_note_from_db(title)
  File "/home/john/files/Cardinal-DH/plugins/notes/plugin.py", line 134, in _get_note_from_db
    if not result[0]:
exceptions.TypeError: 'NoneType' object has no attribute '__getitem__'

Anti-spam plugin

May involve adding to the core of the bot for ignoring commands by users, but the functionality should be in the plugin so as to allow easy editing, whitelists, etc.

per-channel plugin enable/disable

This is useful for things like disabling the URL plugin in channels where another bot has already taken care of the service. Things to take into account:

  1. Plugin's events could trigger other plugins that aren't disabled
  2. Config syntax? Or only available on-the-fly?
  3. If there'll be a way to do it in config, it should be implemented with the new-style config that accompanies the multi-server change (TODO: Create issue on Github)

issues after reconnection

Looks like the Plugin Manager didn't get instantiated correctly, as well as the irc.invite method never got de-registered when Cardinal disconnected. (Basic guess, would have to look deeper in the code to verify.)

2015-03-06 02:25:51,214 - cardinal.bot - INFO - Could not connect ([Failure instance: Traceback (failure with no frames): <class 'twisted.internet.error.ConnectionRefusedError'>: Connection was refused by
 other side: 111: Connection refused.
]), retrying in 300 seconds
2015-03-06 02:30:52,594 - cardinal.bot - INFO - Signed on as Cardinal
2015-03-06 02:30:52,595 - cardinal.bot - INFO - Attempting to identify with NickServ
Unhandled Error
Traceback (most recent call last):
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/policies.py", line 120, in dataReceived
    self.wrappedProtocol.dataReceived(data)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2430, in dataReceived
    basic.LineReceiver.dataReceived(self, data.replace('\r', ''))
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/basic.py", line 571, in dataReceived
    why = self.lineReceived(line)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2438, in lineReceived
    self.handleCommand(command, prefix, params)
--- <exception caught here> ---
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2482, in handleCommand
    method(prefix, params)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 1846, in irc_RPL_WELCOME
    self.signedOn()
  File "/home/john/files/Cardinal-DH/cardinal/bot.py", line 85, in signedOn
    self.event_manager.register("irc.invite", 2)
  File "/home/john/files/Cardinal-DH/cardinal/plugins.py", line 674, in register
    raise EventAlreadyExistsError("Event already exists: %s" % name)
cardinal.exceptions.EventAlreadyExistsError: Event already exists: irc.invite
2015-03-06 02:30:52,608 - cardinal.plugins - INFO - Calling callbacks for event: irc.mode
2015-03-06 02:30:52,680 - cardinal.plugins - INFO - Calling callbacks for event: irc.notice
2015-03-06 02:30:52,774 - cardinal.plugins - INFO - Calling callbacks for event: irc.notice
2015-03-06 02:30:52,851 - cardinal.plugins - INFO - Calling callbacks for event: irc.privmsg
Unhandled Error
Traceback (most recent call last):
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/policies.py", line 120, in dataReceived
    self.wrappedProtocol.dataReceived(data)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2430, in dataReceived
    basic.LineReceiver.dataReceived(self, data.replace('\r', ''))
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/basic.py", line 571, in dataReceived
    why = self.lineReceived(line)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2438, in lineReceived
    self.handleCommand(command, prefix, params)
--- <exception caught here> ---
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2482, in handleCommand
    method(prefix, params)
  File "/home/john/files/Cardinal-DH/cardinal/bot.py", line 139, in irc_PRIVMSG
    self.plugin_manager.call_command(user, channel, message)
exceptions.AttributeError: 'NoneType' object has no attribute 'call_command'
2015-03-07 15:06:26,477 - cardinal.plugins - INFO - Calling callbacks for event: irc.notice
2015-03-12 20:13:51,596 - cardinal.bot - INFO - Connection lost ([Failure instance: Traceback (failure with no frames): <class 'twisted.internet.error.ConnectionDone'>: Connection was closed cleanly.
]), reconnecting in 10 seconds.
2015-03-12 20:14:08,986 - cardinal.bot - INFO - Signed on as Cardinal
2015-03-12 20:14:08,986 - cardinal.bot - INFO - Attempting to identify with NickServ
Unhandled Error
Traceback (most recent call last):
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/policies.py", line 120, in dataReceived
    self.wrappedProtocol.dataReceived(data)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2430, in dataReceived
    basic.LineReceiver.dataReceived(self, data.replace('\r', ''))
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/basic.py", line 571, in dataReceived
    why = self.lineReceived(line)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2438, in lineReceived
    self.handleCommand(command, prefix, params)
--- <exception caught here> ---
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2482, in handleCommand
    method(prefix, params)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 1846, in irc_RPL_WELCOME
    self.signedOn()
  File "/home/john/files/Cardinal-DH/cardinal/bot.py", line 85, in signedOn
    self.event_manager.register("irc.invite", 2)
  File "/home/john/files/Cardinal-DH/cardinal/plugins.py", line 674, in register
    raise EventAlreadyExistsError("Event already exists: %s" % name)
    cardinal.exceptions.EventAlreadyExistsError: Event already exists: irc.invite
2015-03-12 20:14:08,992 - cardinal.plugins - INFO - Calling callbacks for event: irc.mode
2015-03-12 20:14:46,450 - cardinal.plugins - INFO - Calling callbacks for event: irc.notice

Fix possible memory leaks in EventManager with weak references

It's also worth taking a look at the PluginManager and seeing whether weak references may be useful there as well. In the case of the EventManager, callbacks to events should be registered as weak references, so that if the plugin that registered the callback doesn't remove its callback for some reason, it doesn't prevent the garbage collector from handling the plugin (and this would help to prevent accidental duplicate callbacks for reloaded plugins.)

switch to a new source for calculator support

iGoogle was recently shutdown, taking the unofficial Google Calculator API with it. Might switch to Wolfram Alpha. Alternatively I could use a library to add calculator support. Need to look into it.

Move Twisted hooks outside of CardinalBot

Instead, CardinalBot will trigger a new instance of TwistedIRCProtocol, which will hook into CardinalBot. The instance of TwistedIRCProtocol will handle connection to the protocol as well as interfacing with it.

Urban Dictionary plugin

.ud what's up should return the first definition and a permalink to the definitions page.

When Cardinal reconnects, her uptime loses anything over 24 hours.

11:22:40      whoami | .info
11:22:41   +Cardinal | I am a Python-based Cardinal IRC bot. My owners are: whoami. You can find out more about me on my Github page: http://johnmaguire.github.io/Cardinal (Try
                     | .help for commands.)
11:22:41   +Cardinal | I have been online without downtime for 3 days 00:42:18, and was initially brought online 3 days 00:35:04 ago. I've been reloaded (or partially reloaded)
                     | 0 times since then.
11:24:43         <-- | Cardinal ([email protected]) has quit (Killed (NickServ (GHOST command used by [email protected])))
11:24:55         --> | Cardinal ([email protected]) has joined #darchoods
11:24:55          -- | Mode #darchoods [+v Cardinal] by ChanServ
11:24:57      whoami | .info   
11:24:59   +Cardinal | I am a Python-based Cardinal IRC bot. My owners are: whoami. You can find out more about me on my Github page: http://johnmaguire.github.io/Cardinal (Try
                     | .help for commands.)
11:25:00   +Cardinal | I have been online without downtime for 00:00:02, and was initially brought online 00:37:20 ago. I've been reloaded (or partially reloaded) 0 times since
                     | then.

Add URL detection event to URLs plugin

URLs plugin should fire an event when it detects a URL so other plug-ins can hook in and provide additional info.

Some ideas for handlers:
imgur
reddit
YouTube
Wikipedia
Urban Dictionary

Re-evaluate class / instance members

Lots of class members should probably be defined as instance members instead (this really bit me in the butt with #44).

Learning a language by writing an IRC bot means falling prone to the typical gotchas. :(

add info bot plugin

I have to ideas for syntax... either:

Cardinal: Android is a mobile operating system developed by Google.
Cardinal: what is android?
whoami: Android is a mobile operating system developed by Google.

Alternatively...

.dadd android a mobile operating system developed by Google
.define android
whoami: a mobile operating system developed by Google

With the second syntax, it may be beneficial to also make lookups easier with alternative syntax of:

.android
whoami: a mobile operating system developed by Google

If a command exists by the definition name, it would default to the command rather than the definition (with .define serving as the only way to read the definition.)

Don't spam URL titles

I think, at least initially, this will be pretty simple, logic similar to:

if link_to_parse == last_link_parsed and last_link_parsed_time < 5 minutes ago:
    return

This would make talking about sites like goo.gl and Last.fm a lot less annoying.

Users module (track users nick changes, aliases, etc.)

Ideally, I would like to see the following features implemented:

  • User profiles with aliases created under them through watching of nick changes
  • Ability to change which alias is the primary alias
  • Ability to merge two user profiles
  • Ability to separate an alias from a user profile
  • Tracking of vhosts to determine whether a user is a likely alias of another nick
  • Tie into other plugins to provide a better method of tracking user data (for example Last.fm username data would be accessible from any alias)

Help plugin fails when trying to get help for a command

Happened when attempting .help setlastfm

Unhandled Error
Traceback (most recent call last):
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/policies.py", line 120, in dataReceived
    self.wrappedProtocol.dataReceived(data)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2430, in dataReceived
    basic.LineReceiver.dataReceived(self, data.replace('\r', ''))
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/basic.py", line 571, in dataReceived
    why = self.lineReceived(line)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2438, in lineReceived
    self.handleCommand(command, prefix, params)
--- <exception caught here> ---
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2482, in handleCommand
    method(prefix, params)
  File "/home/john/files/Cardinal-DH/cardinal/bot.py", line 139, in irc_PRIVMSG
    self.plugin_manager.call_command(user, channel, message)
  File "/home/john/files/Cardinal-DH/cardinal/plugins.py", line 613, in call_command
    command(self.cardinal, user, channel, message)
  File "/home/john/files/Cardinal-DH/plugins/help/plugin.py", line 89, in help
    help = self._get_command_help(cardinal, command)
  File "/home/john/files/Cardinal-DH/plugins/help/plugin.py", line 39, in _get_command_help
    for name, module in cardinal.loaded_plugins.items():
exceptions.AttributeError: CardinalBot instance has no attribute 'loaded_plugins'

split CardinalBot.py into classes

CardinalBot.py contains all the main bot logic.

It would be much easier and nicer to read if it was split into pieces. I would like to start using proper logging, with the Python logging library, and a separate class for plugin logic at the very least.

This would also be a good time to start making things PEP-8 compatible.

  • Use Python's logging for console output
  • Create a config system for Cardinal in JSON
  • Implement config in plugins
  • Re-implement events

complex URL plugin rules

I would like to add support for, for example, grabbing the amount of time, title, and uploader of YouTube videos, the time and track artist on SoundCloud links, etc.

add more bot meta info

e.g. Number of times reloaded, bot uptime, connected time, latest git commit (for version).

irc_MODE can complain about regex matching

Happened on first connect. Probably because a MODE gets sent to the client with the user being the name of the network (e.g. irc.darchoods.net), which can't be split with *!*@*.

Unhandled Error
Traceback (most recent call last):
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/policies.py", line 120, in dataReceived
    self.wrappedProtocol.dataReceived(data)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2430, in dataReceived
    basic.LineReceiver.dataReceived(self, data.replace('\r', ''))
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/protocols/basic.py", line 571, in dataReceived
    why = self.lineReceived(line)
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2438, in lineReceived
    self.handleCommand(command, prefix, params)
--- <exception caught here> ---
  File "/home/john/files/Cardinal-DH/local/lib/python2.7/site-packages/twisted/words/protocols/irc.py", line 2482, in handleCommand
    method(prefix, params)
  File "/home/john/files/Cardinal-DH/cardinal/bot.py", line 202, in irc_MODE
    (user.group(1), user.group(2), user.group(3), channel, mode)
exceptions.AttributeError: 'NoneType' object has no attribute 'group'

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.