Code Monkey home page Code Monkey logo

bot's Introduction

go-bot

Circle CI GoDoc Coverage Status Go report Reviewed by Hound

IRC, Slack & Telegram bot written in Go using go-ircevent for IRC connectivity, nlopes/slack for Slack and Syfaro/telegram-bot-api for Telegram.

2016-01-17 11 21 38 036

Plugins

Please see the plugins repository for a complete list of plugins.

You can also write your own, it's really simple.

Compiling and testing the bot and plugins (Debug)

This project uses the new Go 1.11 modules if you have Go 1.11 installed, just clone the project and follow the instructions bellow, when you build Go will automatically download all dependencies.

To test the bot, use the debug console app.

  • Clone this repository or use go get github.com/go-chat-bot/bot
  • Build everything: go build ./...
  • Build and execute the debug app:
    • cd debug
    • go build
    • ./debug
  • This will open a console where you can type commands
  • Type !help to see the list of available commands

Testing your plugin

  • Add your plugin to debug/main.go import list
  • Build the debug app
  • Execute it and test with the interactive console

Protocols

Slack

To deploy your go-bot to Slack, you need to:

  • Create a new bot user integration on Slack and get your token
  • Import the package github.com/go-chat-bot/bot/slack
  • Import the commands you would like to use
  • Call slack.Run(token)

Here is a full example reading the Slack token from the SLACK_TOKEN env var:

package main

import (
    "os"

    "github.com/go-chat-bot/bot/slack"
    _ "github.com/go-chat-bot/plugins/catfacts"
    _ "github.com/go-chat-bot/plugins/catgif"
    _ "github.com/go-chat-bot/plugins/chucknorris"
    // Import all the commands you wish to use
)

func main() {
    slack.Run(os.Getenv("SLACK_TOKEN"))
}

IRC

To deploy your own go-bot to IRC, you need to:

  • Import the package github.com/go-chat-bot/bot/irc
  • Import the commands you would like to use
  • Fill the Config struct
  • Call irc.Run(config)

Here is a full example:

package main

import (
	"github.com/go-chat-bot/bot/irc"
	_ "github.com/go-chat-bot/plugins/catfacts"
	_ "github.com/go-chat-bot/plugins/catgif"
	_ "github.com/go-chat-bot/plugins/chucknorris"
	// Import all the commands you wish to use
	"os"
	"strings"
)

func main() {
	irc.Run(&irc.Config{
		Server:   os.Getenv("IRC_SERVER"),
		Channels: strings.Split(os.Getenv("IRC_CHANNELS"), ","),
		User:     os.Getenv("IRC_USER"),
		Nick:     os.Getenv("IRC_NICK"),
		Password: os.Getenv("IRC_PASSWORD"),
		UseTLS:   true,
		Debug:    os.Getenv("DEBUG") != "",})
}

To join channels with passwords just put the password after the channel name separated by a space:

Channels: []string{"#mychannel mypassword", "#go-bot"}

Telegram

To deploy your go-bot to Telegram, you need to:

  • Follow Telegram instructions to create a new bot user and get your token
  • Import the package github.com/go-chat-bot/bot/telegram
  • Import the commands you would like to use
  • Call telegram.Run(token, debug)

Here is a full example reading the telegram token from the TELEGRAM_TOKEN env var:

package main

import (
    "os"

    "github.com/go-chat-bot/bot/telegram"
    _ "github.com/go-chat-bot/plugins/catfacts"
    _ "github.com/go-chat-bot/plugins/catgif"
    _ "github.com/go-chat-bot/plugins/chucknorris"
    // Import all the commands you wish to use
)

func main() {
    telegram.Run(os.Getenv("TELEGRAM_TOKEN"), os.Getenv("DEBUG") != "")
}

Rocket.chat

To deploy your go-bot to Rocket.chat, you need to:

  • Import the package github.com/go-chat-bot/bot/rocket
  • Import the commands you would like to use
  • Call rocket.Run(config)

Here is a full example:

package main

import (
	"os"

	"github.com/go-chat-bot/bot/rocket"
	_ "github.com/go-chat-bot/plugins/godoc"
	_ "github.com/go-chat-bot/plugins/catfacts"
	_ "github.com/go-chat-bot/plugins/catgif"
	_ "github.com/go-chat-bot/plugins/chucknorris"
)

func main() {
	config := &rocket.Config{
		Server:   os.Getenv("ROCKET_SERVER"),
		Port:     os.Getenv("ROCKET_PORT"),
		User:     os.Getenv("ROCKET_USER"),
		Email:    os.Getenv("ROCKET_EMAIL"),
		Password: os.Getenv("ROCKET_PASSWORD"),
		UseTLS:   false,
		Debug:    os.Getenv("DEBUG") != "",
	}
	rocket.Run(config)
}

Google Chat

To deploy your go-bot to Google Chat (also known as Hangouts Chat, not plain Hangouts) you will first need to follow documentation to setup pub/sub project in Google Cloud. This will enable your bot to receive messages even when it is behind a firewall.

Condensed, the steps you will need to take are as follows:

  • Create new project in google cloud console
    • ID of the project will be used in Config.PubSubProject
  • Create service credentials for this project
    • Path to downloaded credentials file should be in env variable GOOGLE_APPLICATION_CREDENTIALS
    • Choose "Pub/Sub Editor" role for the credential
  • Enable Pub/Sub API in cloud console
  • Create new topic in the Pub/Sub (say "google-chat")
    • Use the value of "Topic Name" (not "Topic ID") for Config.TopicName (e.g. /project/<proj_name>/topics/<topic_name>)
  • Modify permissions on created topic so that "[email protected]" has Pub/Sub Publisher permissions
  • Enable hangouts chat api in Cloud Console
  • Go to hangouts chat API config in the Cloud Console and fill in info
    • Connection settings - use Pub/Sub and fill in topic string you created above

Config.SubscriptionName should be unique for each environment or you'll not process messages correctly. If you encounter issues make sure your credentials are correct and permissions for topics/queues are set up correctly.

Config.WelcomeMessage is sent each time the bot joins a new room or private chat.

Full example is here:

package main

import (
	"os"

	"github.com/go-chat-bot/bot/google-chat"
	_ "github.com/go-chat-bot/plugins/godoc"
	_ "github.com/go-chat-bot/plugins/catfacts"
	_ "github.com/go-chat-bot/plugins/catgif"
	_ "github.com/go-chat-bot/plugins/chucknorris"
)

func main() {
	googlechat.Run(&googlechat.Config{
		PubSubProject:    os.Getenv("HANGOUTS_PROJECT"),
		TopicName:        os.Getenv("HANGOUTS_TOPIC"),
		SubscriptionName: os.Getenv("HANGOUTS_SUB"),
		WelcomeMessage:   os.Getenv("HANGOUTS_WELCOME"),
}

Deploying your own bot

To see an example project on how to deploy your bot, please see my own configuration:

bot's People

Contributors

1uka avatar alex77g avatar aspic avatar badnetmask avatar bnfinet avatar doenietzomoeilijk avatar dontmindmes avatar dukex avatar fabioxgn avatar icco avatar jrwren avatar marclanepitt avatar martinusso avatar matlockx avatar mbaynton avatar mbombonato avatar neapolitanicecream avatar nloadholtes avatar oddlid avatar pasali avatar pyinx avatar reestr avatar robertoschneiders avatar ruseinov avatar salbertson avatar seguer avatar toolateforteddy avatar yitsushi avatar zachomedia 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

bot's Issues

expose command prefix in `irc.Config`

expose the command prefix variable in the Config struct, the current OS ENV solution feels like a cheat, plus it doesn't apply for plugins and is not documented.

let me know if I could send a PR w/ the change.

Support slash commands on Telegram

Telegram bots by default run in "privacy mode" which only passes commands starting with a forward slash / to bots.

Right now the bot to run on telegram needs privacy mode off, but this is easy fixed just changing the prefix for commands on telegram to /.

rocket chat not working

It appears, that the library to access rocket chat has been abandoned and uses deprecated parts of the rocket chat api.

Support Reaction Handling

There should be a parallel routing and message handling pipeline for emoji reactions. Both Slack and RocketChat fully support reactions.

Alternatively, we could expand our internal representation of an incoming message to also handle a reaction? One issue with this is that reactions are a much simpler message type, and yet also support both ReactionAdded and ReactionRemoved Events. As such, I think we would be better served by just having a different router object entirely.

Maybe orthogonal to this effort, we should make it much easier to attach a reaction to message as part of a response to a message. I am finding that in popular channels, having to respond inline, or even in a thread, is much more disruptive than being able to simply add a reaction.

Filter html markdown

I am using gcr.io on the chat text and slack interprets it as a link that makes resulting command with <http://gcr.io|gcr.io> Anyway to escape this markdown? I tried with ` also but no way.

Multi-line response from plugin

Is there any way to return a multi-line response from a plugin? Currently if I put a newline in the string then only the first line displays:
msg = fmt.Sprintf("Can we do\nmulti line return?")

Make using Unidecode optional

The use of unidecode.Unidecode(c.RawArgs) in parser.go can have unwanted effects when one wants to save user input in languages with letters not in ASCII.
It would be nice to have the option to disable it.

Expand with PeriodicCommands?

Hi, and thanks for this neat little bot! Sorry to open an issue but I wanted a place to discuss a little feature I've been thinking about. It's basically a new type of command 'PeriodicCommands' (a better name is welcome), which are commands run after some duration, or at a specific time (like a cron job).

My use case for this would be checking some logs every X minute, and if some error occurs, post a message to the provided channel/user.

The easiest way to accomplish this would be to add this new type of commands, as well as a RegisterPeriodicCommand function (similar to RegisterCommand). And in the loop check whether or not to run any of these periodic commands. Do you have any thoughts or different ideas to achieve the same goal? :-) I would be happy to code up a PR if you think this is something we should add to the bot.

  • Kjetil

Export underlying ircevent.Connection

It would be nice to be able to get a handle to the underlying ircevent.Connection, so once could call methods on that.
As an example, I'd like to be able to set up a signal handler and call Quit() on the connection.

Support text formatting for Slack

If a command is sent with bold message formatting the logic doesn't seem to match it to a plugin. I would assume this has to do with the formatting of the message being surrounded in * characters. Is there an option to remove formatting or continue to route the message to the plugins if formatting is present?

Disable or rename !help by config

Hi, any chance of considering this feature request so the !help handler can be disabled or renamed from configuration? In a channel of multiple bots it can get noisy when !help is called.

Sending message while processing command

How can i send information messages for commands which takes long time to finish. For example: I have a command that does three things; clone git repository, build project and publish it. In that case i want to inform user for each stage like "cloning repo...", "building project..." and "publishing project..."

Getting two responses for single slack command

I am building slack bot which basically takes kubectl command like kubectl get pods. Now, I want to send some message like Your request has been acknowledged immediately after any command is sent from slack and then the result of command. Currently, I have implemented the command result sending process but couldn't figure out how to send the "pre result message". Any idea? I tried implementing this way:

func response(command *bot.PassiveCmd) (string, error) {
   return fmt.Sprintf("Your request has been added to queue"), nil
}
func init() {
	bot.RegisterPassiveCommand(
					"",
					response)
}
func init() {
	bot.RegisterCommand(
 		"deployer",
 		"Kubectl Slack integration",
 		"",
	kubectl)
 
}

But the problem is when I enter slack message with @deployer get pods, then it triggers kubectl and when I just hit kubectl get pods, it triggers response. But I need to combine both. PS: Here, my cmdprefix is @. Hope you get it. If I am not clear, please feel free to ask on comment. Thanks.

build: error finding git.apache.org/thrift.git

Hi,

I'm not sure how to debug this issue on my side, I'm just trying to build everything in order to build a new bot (it might not be the best practice), and this process blocks on this error:

$ go build ./...
go: finding git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999
go: git.apache.org/[email protected]: git fetch -f https://git.apache.org/thrift.git refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /home/alx/go/pkg/mod/cache/vcs/83dba939f95a790e497d565fc4418400145a1a514f955fa052f662d56e920c3e: exit status 128:
        fatal: unable to access 'https://git.apache.org/thrift.git/': Failed to connect to git.apache.org port 443: Connection timed out
go: error loading module requirements

Thanks,

Alex

Rocket.Chat default port for a bot?

I'm setting up a bot to connect to a server which doesn't belong to me
and this server is using the default settings for almost everything including ports.
Here's my current config:

...
func main() {
        config := &rocket.Config{
                Server:   "chat.server.com",
                Port:     "?????",
                User:     "test-1-bot",
                Email:    "[email protected]",
                Password: "********",
                UseTLS:   false,
                Debug:    os.Getenv("DEBUG") != "",
        }
        rocket.Run(config)
}

What is a default ROCKET_PORT value: 3000, 80 or 443 ?
I tried using them all, but am getting the following results:

3000: login err: Post http://chat.server.com:3000/api/v1/login: dial tcp ...*:3000: connect: connection refused
80: login err: Request error: 405 Method Not Allowed
443: login err: Request error: 400 Bad Request

Telegram api error exit status 1

package main

import (
    "os"
    "github.com/go-chat-bot/bot/telegram"
)

func main() {
    telegram.Run(os.Getenv("telegram__tokenhere"), os.Getenv("DEBUG") != "")
}

The result I received is:

exit status 1

My api token is working on other frameworks.

Example IRC config is no longer bot.Config, but irc.Config

I was recently trying out the bot for IRC, and noticed that I kept getting build errors for bot.Config, such as undefined: bot.Config. After looking at the heroku example, it was using &irc.Config. This was not changed in the README for this repository.

IRC: Missing RealName on events

I'd like to use go-chat-bot for IRC purposes, but I'm running into a snag. I can solve the snag, but I'm looking for some input on the best way to solve it.

The issue I'm running into is the fact that in irc/irc.go, on a privmsg event, the User is created with only his nick. If I want some command to be limited to certain user/host combo's, I'm out of luck, since I only have the nick to work with. As I see it, there's three ways of solving it:

  • Stuff the username/host into User.RealName. This works, but it's slightly ugly since we're stuffing something that's not a RealName into a RealName.
  • Add host info to the User, for example User.Host. I'm not sure how that'd impact other networks, if at all. I don't know if you get that kind of info from Slack / Telegram, and whether they make any sense there. Still, this seems like the best solution to me.
  • Do an IRC whois inside my command to validate the user. This would work (assuming I can do that from within the bot, I haven't looked into it that much), but it's wasteful as it results in more IRC traffic on each request. Last-ditch resort, I'm really not a fan of this one.

I think the second option would be the best one; it'd involve adding some properties to User that might go unused for non-IRC networks - I don't think that's a huge problem, but input on this would be welcome.

I can come up with a pull request, of course.

Send a message to a specific adapter

What I'd like to do is create a slack/irc bridge. It would be great if we could have access to a specific network connection, such that I could get a message from one network and post it to the other (and vice-versa).

Do you have any thoughts on how I might make a pull request to add that functionality?

Case insensitive commands

The bot now just accepts case-sensitive commands, like: !cotacao, it would be nice to undestand !Cotacao and even !Cotação.

cannot use message (type string) as type slack.MsgOption in argument to api.PostMessage

api.PostMessage(target, message, params)

`D:\Go\GOPATH\src\github.com\go-chat-bot>go build ./...

github.com/go-chat-bot/bot/slack

bot\slack\slack.go:32:17: cannot use message (type string) as type slack.MsgOption in argument to api.PostMessage
bot\slack\slack.go:32:17: cannot use params (type slack.PostMessageParameters) as type slack.MsgOption in argument to api.PostMessage`

integration of RegisterMessageReceiveFilter work

A while back I added the feature RegisterMessageReceiveFilter to my fork...
https://github.com/bnfinet/go-chat-bot/blob/master/cmd.go#L282-L296

This filter operation is conducted before command interpretation and execution. This allows for plugins to be created with a generalized allow/disallow and RBAC of commands based on proto/server/channel/user criteria.

Instead of opening a PR I decided to start with an issue to see if this is worthy of inclusion. If so, I'll start from the current master in this repo and (non git) cherry pick the changes, since this work is mostly from a few years back.

Thanks for your consideration.

5271579

Wirbeltier

import random

Definieren der Klassen mit Merkmalen und Beispielen

classes = {
"Fische": {
"Merkmale": "Fische sind Wirbeltiere mit Kiemen, Schuppen und Schwimmblasen.",
"Beispiele": ["Lachs", "Forelle", "Haifisch", "Seepferdchen"]
},
"Amphibien": {
"Merkmale": "Amphibien sind Wirbeltiere, die im Wasser und an Land leben können und feuchte, schuppige Haut haben.",
"Beispiele": ["Kröte", "Frosch", "Salamander", "Axolotl"]
},
"Reptilien": {
"Merkmale": "Reptilien haben trockene Haut mit Schuppen oder Platten und legen Eier mit einer harten Schale.",
"Beispiele": ["Krokodil", "Schlange", "Eidechse", "Schildkröte"]
},
"Vögel": {
"Merkmale": "Vögel haben Federn, einen harten Schnabel und legen Eier.",
"Beispiele": ["Adler", "Papagei", "Pinguin", "Kolibri"]
},
"Säugetiere": {
"Merkmale": "Säugetiere haben eine weiche Haut, Haare oder Fell, und produzieren Milch, um ihre Jungen zu ernähren.",
"Beispiele": ["Elefant", "Fledermaus", "Delphin", "Affe"]
}
}

Begrüßungsnachricht

def greet():
print("Hallo! Ich bin ein Chatbot, der Fragen über Wirbeltiere beantworten kann. Welche Klasse von Wirbeltieren interessiert Sie?")

Antwort auf die Frage des Benutzers

def respond(message):
for key in classes:
if key.lower() in message.lower():
reply = classes[key]["Merkmale"] + " Einige Beispiele für " + key + " sind " + ", ".join(classes[key]["Beispiele"]) + "."
return reply
return "Es tut mir leid, ich habe Ihre Frage nicht verstanden. Bitte stellen Sie eine andere Frage oder fragen Sie nach einer anderen Wirbeltierklasse."

Chat-Schleife

greet()
while True:
message = input("> ")
if message.lower() == "exit":
break
else:
print(respond(message))

Support quoted arguments

The current argument parser uses a simple algorithm of splitting on the space character. However, there are many instances where supporting an argument that contained the space character would be ideal. To support these cases quoted strings within a command should be treated as a single argument.

Provide a way to change command prefix

It will be useful to give ability to override bots command prefix. I want to use prefixs other than "!" character or maybe bots name while i am typing commands.

Add new command type: filter

So I have 2 use cases that would need new type of command.

Use cases:

  • When URLs are posted by the bot automatically use shortener on them
  • Provide ability to silence the bot for period of time in specific channel

The way I imagine this working is as follows:

  • Add a new "filter" command registration
  • Filters get called around/inside SendMessage invocations

They can either "skip" the message (second use case) or modify the output (first use case). It seems like a nicer approach then for example implementing shortener in JIRA and/or other plugins.

Thoughts?

General debug mode

Hi, I'm currently trying to migrate a hubot powered slackbot away from its underlying hubot (getting tired of writing coffeescript and using node).

One thing I'm kinda missing from hubot is the local debug mode. Hubot bundles an executable that starts a local instance of the bot completely inside the shell where the user can interact with the bot trying out various functionality and debugging plugins. For testing purposes, this "room" is visible to both the user and the bot as "#shell" with the user also being called "shell" by the bot.

Integrating something along the lines of that here should probably result in a new adapter or protocol as you call it in /debug possibly? I'm not really versed yet in all things go but would love to try myself at creating a PR for a feature like that. Any possible input would be greatly appreciated though!

Errors are shown publicly

Parsing errors are shown publicly, which is kinda odd considering that "command not found", which undoubtably is thrown more frequent, is only logged.

I believe these lines are at fault, https://github.com/go-chat-bot/bot/blob/master/bot.go#L183-L187

Patch attached:

Index: bot.go
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- bot.go	(revision 3da6cae4547763aa55828a9f75ed4693bed5d36f)
+++ bot.go	(date 1574671039000)
@@ -180,11 +180,7 @@
 func (b *Bot) MessageReceived(channel *ChannelData, message *Message, sender *User) {
 	command, err := parse(message, channel, sender)
 	if err != nil {
-		b.SendMessage(OutgoingMessage{
-			Target:  channel.Channel,
-			Message: err.Error(),
-			Sender:  sender,
-		})
+		log.Fatalf("Error: %v, Channel: %v, Sender: %v", channel.Channel, err.Error(), sender.ID)
 		return
 	}
 

Send message on event

I currently developing a bot that monitor a websocket and forward messages received to a channel. Your library looks great to does this task and will allow to add more feature but the issue I get with is I don't find how to send a message without being into the context of a command most of the method to do that are a not exported. I would like to know if you have any idea how I can achieve this into a plugin?

Tweaks for periodic commands (allow periodic command without predefined channel)

I've mentioned this in the original JIRA plugin issue to some degree - I am planning (one final) feature that would query JIRA for issues that were created/resolved in given project and announce them to a channel.

To do this I was planning to use periodicCommands. However - looking at the code the way they work means the JIRA query would be run for every channel (even if multiple channels receive notifications of the same project).

What I would instead like to do is have a periodicCommand (v2?) that runs the queries (in JIRA case configured by ENV vars) and can send a message to any channel depending on which new/resolved issues it found. I was thinking of doing something like this:

// PeriodicConfig holds a cron specification for periodically notifying the configured channels
type PeriodicConfig struct {
        Version     int     <---- NEW
        CronSpec string                               // CronSpec that schedules some function
        Channels []string                             // A list of channels to notify
        CmdFunc  func(channel string) (string, error) // func to be executed at the period specified on CronSpec
        CmdFuncV2 func() ([]CmdResult, error) <--- NEW
}

And of course, tweak the bot.go to account for this so that when V2 func is called it will send all messages. There are other ways to do this of course so before I go down that road - any preferences/ideas?

Slack - Get the channel name

First, thanks for open sourcing this chat bot!

I'm working on a small bot that connects to our Slack, but I don't know how to get the channel name. Currently I only get the user (nickname and name), channel (id) and the command.

Is there any way to get the channel name?

Make command registration goroutine safe

We should add a mutex around all places that register commands. By doing so, it would be much easer to register functions dynamically.

For example:
!send-periodically "I like cats" "0 0 0 * * *"
Would then send, in the current channel, "I like cats" at midnight every day.

I am sure more interesting things will be considered by allowing dynamic registration of commands, and making the registration threadsafe is just the first step to enable that excellence.

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.