lrstanley / girc Goto Github PK
View Code? Open in Web Editor NEW:bomb: girc is a flexible IRC library for Go :ok_hand:
Home Page: https://pkg.go.dev/github.com/lrstanley/girc
License: MIT License
:bomb: girc is a flexible IRC library for Go :ok_hand:
Home Page: https://pkg.go.dev/github.com/lrstanley/girc
License: MIT License
This document specifies the standard
FAIL
,WARN
, andNOTE
messages, intended to provide a clean, consistent interface for sending general errors, warnings, and informational messages to clients. Implementers should not need to reserve new numerics for error, warning, or general informational messages, especially as numerics themselves and the mapping of numerics to names can be unclear or conflicting.The
FAIL
message indicates a complete failure to process a given command/function, or simply some error about the current session that clients should be aware of.The
WARN
message indicates some non-fatal feedback about a given command/function, or some less vital feedback on the current session.The
NOTE
message indicates some informational message about a given command/function, or about the current session.
Once support is added, the IRCv3 Library Support page needs to be updated to include these features.
Currently running go test -race
gives out at least one data race warning, I've personally identified another one in the ping/pong code that writes/reads to the struct variables without synchronization.
There might be more, since I don't know how extensive the current test code is.
Need to look into implementing fuzzing using 1.18's new functionality:
I imagine this will for sure uncover additional bugs.
Presently, girc does not include any heuristics to detect netsplits. Therefore, after a netsplit, the client may remain on the splitted network. I think it would be handy if girc would detect netsplits and attempt a reconnect in the case of a netsplit.
No.
N/A
No
For reference, the IRC netsplit detection heuristic is available here: https://github.com/irssi/irssi/blob/master/src/irc/core/netsplit.c
for example, passing to golang.org/x/net/proxy
?
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x30 pc=0xb23397]
goroutine 4248 [running]:
github.com/lrstanley/girc.(*Client).readLoop(0xc000454a80, {0x1722cb8, 0xc0005704b0})
/home/allbery/go/pkg/mod/github.com/lrstanley/[email protected]/conn.go:440 +0x297
github.com/lrstanley/girc/internal/ctxgroup.(*Group).Go.func1()
/home/allbery/go/pkg/mod/github.com/lrstanley/[email protected]/internal/ctxgroup/ctxgroup.go:58 +0x63
created by github.com/lrstanley/girc/internal/ctxgroup.(*Group).Go in goroutine 31
/home/allbery/go/pkg/mod/github.com/lrstanley/[email protected]/internal/ctxgroup/ctxgroup.go:55 +0x79
This is happening with matterbridge, both latest release and git versions, but doesn't appear to be related directly to matterbridge. It also doesn't appear to be the same problem as #14 because the bridge has been quiescent aside from the reconnect, and the line numbers/traceback differ.
I expect the IRC client to successfully resume after reconnecting,
No response
v0.0.0-20230729130341-dd5853a5f1a6
linux/ubuntu
I have not as yet tried rebuilding matterbridge with the version on pkg.go.dev
.
If it matters, the specific Ubuntu release is 22.04.2.
The default CTCP handlers are currently registered unconditionally in client.New
. The CTCP handlers disclose quite a few things, e.g. current Time (through handleCTCPTime
) and operating system, architecture and girc version (through handleCTCPVersion
).
IMHO it would be nice if these handlers could be disabled. For instance through a new Config
option.
I have stumbled upon the code here, where you are using string
(as in map[string][]string
) and nil
, instead of struct{}
and struct{}{}
. From a quick glance I cannot see you using the value anywhere, and using struct{}
would communicate this intent better, therefore making the codebase slightly cleaner, and additionally it would consume less memory, but this memory consumption is negligible either way.
From:
var possibleCap = map[string][]string{
"account-notify": nil,
[...]
}
to:
var possibleCap = map[string]struct{}{
"account-notify": struct{}{},
[...]
}
You may also like to compare the assembly output: https://go.godbolt.org/z/4IMjlE
After making a simple client and building with -race, a data race was found rather fast involving the ping/pong handling code.
==================
WARNING: DATA RACE
Read at 0x00c0420f2090 by goroutine 14:
github.com/lrstanley/girc.(*Client).pingLoop()
F:/goenv/src/github.com/lrstanley/girc/conn.go:546 +0x5b0
Previous write at 0x00c0420f2090 by goroutine 53:
github.com/lrstanley/girc.handlePONG()
F:/goenv/src/github.com/lrstanley/girc/builtin.go:116 +0x91
github.com/lrstanley/girc.HandlerFunc.Execute()
F:/goenv/src/github.com/lrstanley/girc/handler.go:67 +0x77
github.com/lrstanley/girc.(*Caller).exec.func1()
F:/goenv/src/github.com/lrstanley/girc/handler.go:231 +0x476
Goroutine 14 (running) created at:
github.com/lrstanley/girc.(*Client).internalConnect()
F:/goenv/src/github.com/lrstanley/girc/conn.go:284 +0x3b7
github.com/lrstanley/girc.(*Client).Connect()
F:/goenv/src/github.com/lrstanley/girc/conn.go:184 +0x4c
main.main()
F:/goenv/src/github.com/R-a-dio/valkyrie/cmd/hanyuu/main.go:33 +0x195
Goroutine 53 (finished) created at:
github.com/lrstanley/girc.(*Caller).exec()
F:/goenv/src/github.com/lrstanley/girc/handler.go:209 +0x276
github.com/lrstanley/girc.(*Client).RunHandlers()
F:/goenv/src/github.com/lrstanley/girc/handler.go:45 +0x40a
github.com/lrstanley/girc.(*Client).execLoop()
F:/goenv/src/github.com/lrstanley/girc/client.go:345 +0x18a
==================
I'm trying to connect to freenode with SASL, but it isn't working.
The CAP LS doesn't get any response.
It looks like freenode expects CAP LS before NICK/USER
When I moved c.listCAP()
in conn.go before the NICK/USER writes it works.
https://tip.golang.org/doc/go1.12#compiler will be implemented soon.
The IRC RFC says this about the format of commands:
<params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
<middle> ::= <Any *non-empty* sequence of octets not including SPACE
or NUL or CR or LF, the first of which may not be ':'>
<trailing> ::= <Any, possibly *empty*, sequence of octets not including
NUL or CR or LF>
and
2) After extracting the parameter list, all parameters are equal,
whether matched by <middle> or <trailing>. <Trailing> is just
a syntactic trick to allow SPACE within parameter.
In essence, there is no semantic difference between an argument parsed as a 'middle' or one as a 'trailing'. Trailing is simply a syntactic specification to allow the last argument in a command to contain spaces, if so desired.
However, girc doesn't follow this footnote and explicitly encodes 'trailing' as some special kind of command parameter. This is incorrect; if a command has a 'trailing' parameter, this is simply the last parameter of the command. There is no special meaning to it.
This makes the library cumbersome to use at best (since special care must be taken in regards to 'empty trailing', which should not be necessary at all), and broken at worst (since IRCds are within their rights to send commands for which you would 'expect' a trailing argument without one, such as PRIVMSG, or send commands with a trailing argument which you would not expect to have one).
This is IMHO a very huge flaw in the library and should be fixed before it goes 1.0.
would require rewrite of user existance logic, could also help track user modes.
Hi,
I'm using the girc library in one of my projects and just added some code from the girc examples to reconnecting to the IRC server.
This works perfectly fine and when the connection is lost, he reconencts automatically after the given delay. But the problem is, that when a other goroutine tries to call e.g. Cmd.Message()
to send something during the time between the connection is lost and girc reconnects, it ends up with a SIGSEGV and crashes. See the attached stacktrace
I'm not sure if this is a library problem of girc or if I should catch this and don't use the client during reconenction.
Please let me know if you need more informations to debug this.
Best regards,
Felix
2018/08/15 22:09:11 Connection to irc.hackint.eu:6667 terminated: timed out waiting for a requested PING response
2018/08/15 22:09:11 Reconnecting to irc.hackint.eu:6667 in 30 seconds...
<other gorouting calls Cmd.Message() at this moment>
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x7c03ff]
goroutine 18 [running]:
github.com/lrstanley/girc.(*ircConn).rate(0x0, 0x15, 0x0)
/home/fleaz/workspace/go/src/github.com/lrstanley/girc/conn.go:411 +0x2f
github.com/lrstanley/girc.(*Client).Send(0xc42018e000, 0xc420310770)
/home/fleaz/workspace/go/src/github.com/lrstanley/girc/conn.go:389 +0x17d
github.com/lrstanley/girc.(*Commands).Message(0xc42017a030, 0xc420024a24, 0x6, 0xc4203331c0, 0x5)
/home/fleaz/workspace/go/src/github.com/lrstanley/girc/commands.go:108 +0xf5
main.channelReceiver()
/home/fleaz/workspace/go/src/github.com/f-breidenstein/CptHook/irc.go:106 +0xe8
created by main.ircConnection
/home/fleaz/workspace/go/src/github.com/f-breidenstein/CptHook/irc.go:87 +0x339
Somewhat related to https://github.com/lrstanley/girc/projects/1#card-6997933
Functions like IsValidNick
and IsValidUser
exist:
Lines 214 to 241 in 51b8e09
Lines 243 to 286 in 51b8e09
It would be useful if we could abstract this into an interface NickValidator
.
My particular use-case is that we have a customised IRC server that allows certain users to use the tilde (~
) character β discord puppets have a ~d
suffix. See qaisjp/go-discord-irc.
Would you be willing to accept a pull request that implements this?
(This would prevent the need to fork your repo.)
From RFC 2812:
IRC messages are always lines of characters terminated with a CR-LF (Carriage Return - Line Feed) pair, and these messages SHALL NOT exceed 512 characters in length, counting all characters including the trailing CR-LF.
As I understand it, this is a restriction which applies to messages sent by the server. The client needs to make sure that sent client messages do not result in server messages which exceed this limit. This requires some heuristics to determine the length of the resulting server message for a given client message. Which, in turn, requires calculating the maximum nick and host length et cetera. I personally think that this is a major defect of the IRC protocol itself as it requires implementing "dodgy hacks for every single command" which may exceed this message length. As such, this has also been discussed by the IRv3 working group [1].
Short-term it would nice to implement splitting of messages which exceed this limit. For instance, if a PRIVMSG exceeds it, it has to be split into two PRIVMSG commands. I started working on such an implementation in my own IRC client, which uses girc, however I personally think that message splitting would be best implemented in girc itself. What are your thoughts on this?
Existing "message splitting" implementations:
Maybe it would make sense to start with an implementation of message splitting for PRIVMSGs and maybe extend it to other commands afterwards?
This specification adds a new message tag sent by clients and repeated by servers to correlate responses with a specific request.
Certain client actions can result in responses from the server that vary in interpretation depending on how they were triggered, or otherwise lack a robust way to correlate with local state. Clients have historically needed to keep track of additional local state and/or apply comparison heuristics to server responses to correlate these appropriately.
Labeled responses enable a much simpler form of correlation by using a single id attached to a client request and repeated by the server in its response.
Additionally, labeled responses allow bouncers with multiple connected clients to direct responses (such as WHOIS queries or error messages, see examples) to the correct recipient.
Once support is added, the IRCv3 Library Support page needs to be updated to include these features.
Perhaps the extra colon could be omitted when no reason is given.
https://github.com/lrstanley/girc/blob/a5d4d6aadaa1cbf635cbc213818c39529a1c3ce2/event.go#L390..L392
if e.Command == KICK && len(e.Params) == 2 {
if len(e.Trailing) > 0 {
return fmt.Sprintf("[%s] *** %s has kicked %s: %s", e.Params[0], e.Source.Name, e.Params[1], e.Trailing), true
} else {
return fmt.Sprintf("[%s] *** %s has kicked %s", e.Params[0], e.Source.Name, e.Params[1]), true
}
}
Once I've solidified how I want https://github.com/lrstanley/ircdef to look, may want to look at moving to it for girc. Would definitely be a big change.
When calling Cmd.Message() with a message containing a newline, the newline is stripped, which smashes words together.
The newline should be replaced with a space, instead of just stripped
Call Cmd.Message("#foo", "example\nmessage")
and observe that the resulting message is "examplemessage" instead of "example message"
v0.0.0-20220410132120-49de39aea653
linux/other
I'm using girc via https://github.com/RITlug/teleirc . When a telegram message contains a newline, the resulting IRC message has this problem. It could be worked around in TeleIRC, but it seemed like girc would be a better place to fix it for everyone.
I'm not sure the best way to handle \r or \r\n - ideally collapsing runs of either to just one space?
At https://godoc.org/github.com/lrstanley/girc#Event, EmptyTrailign
should be EmptyTrailing
.
I also recommend replacing all occurrences of rfc
to RFC
, as it is an abbreviation.
There is also an inconsistent use of contractions, somewhere you are using the full form, somewhere you are using the contracted form.
I currently use https://github.com/thoj/go-ircevent/ where I implemented WebIRC support in thoj/go-ircevent#96.
I am interested in girc and would like to add WebIRC support.
Is this fine?
The problem with performing ToRFC1459()
on the nick before storing it in the User struct is that (among other things) capitalization is lost. So function like client.UserList
always return all nicks in lowercase.
I am personally using the values return by channel.UserList
for tab completions and to me it is kind of annoying that the capitalization of nicks as shown by event.Pretty()
differs from the one suggested by the tab completions I have implemented.
I would suggest that nicks are not stored in an RFC 1459 compliant format since any nick can be made RFC 1459 compliant using the ToRFC1459
function on demand but converting it back to its original format isn't possible.
Following 42wim/matterbridge#2037 commit 8240917 causes matterbridge not connect to twitch.tv on irc protocol properly.
Using matterbridge-1.26.0-linux-64bit and this config
[irc.tv]
Password="mytokenwasreplacedofc"
Nick="Somenick"
Server="irc.chat.twitch.tv:6697"
UseTLS=true
RemoteNickFormat="<{NICK}> "
Charset="utf-8"
I get this error:
time="2023-04-04T11:26:18Z" level=error msg="disconnect: error: EOF" prefix=irc
time="2023-04-04T11:26:18Z" level=fatal msg="Starting gateway failed: Bridge irc.tv failed to start: connection failed EOF" prefix=main
Downgrading to matterbridge-1.25.2-linux-64bit does not produce the same error. This only difference between those two versions is the commit mentioned above. The matterbridge folks advised me how to compile without this commit: 42wim/matterbridge#2037 (comment) and it worked as expected.
The same matterbridge connects to irc.libera.chat without problems, so it seems something twitch specific.
Here you can see a successful join to irc.libera.chat and then starts the join to twitch. Matterbridge with --debug option:
time="2023-04-06T21:56:28Z" level=debug msg=""CLIENT_CONNECTED irc.libera.chat:6667"" func=handleOther file="bridge/irc/handlers.go:170" prefix=irc
time="2023-04-06T21:56:32Z" level=info msg="Connection succeeded" func=Connect file="bridge/irc/irc.go:107" prefix=irc
time="2023-04-06T21:56:32Z" level=info msg="irc.yl: joining #your-land-web (ID: #your-land-webirc.yl)" func=joinChannels file="bridge/bridge.go:77" prefix=irc
time="2023-04-06T21:56:32Z" level=debug msg=""@time=2023-04-06T21:56:26.913Z :YL MODE YL +iw"" func=handleOther file="bridge/irc/handlers.go:170" prefix=irc
time="2023-04-06T21:56:32Z" level=info msg="irc.yl: joining #your-land-chat (ID: #your-land-chatirc.yl)" func=joinChannels file="bridge/bridge.go:77" prefix=irc
time="2023-04-06T21:56:32Z" level=info msg="irc.yl: joining #your-flight-chat (ID: #your-flight-chatirc.yl)" func=joinChannels file="bridge/bridge.go:77" prefix=irc
time="2023-04-06T21:56:32Z" level=info msg="irc.yl: joining #your-land-dev (ID: #your-land-devirc.yl)" func=joinChannels file="bridge/bridge.go:77" prefix=irc
time="2023-04-06T21:56:32Z" level=info msg="Starting bridge: irc.tv " func=Start file="gateway/router.go:75" prefix=router
time="2023-04-06T21:56:32Z" level=info msg="Connecting irc.chat.twitch.tv:6697" func=Connect file="bridge/irc/irc.go:81" prefix=irc
time="2023-04-06T21:56:32Z" level=debug msg="setting pingdelay to 1m0s" func=getClient file="bridge/irc/irc.go:306" prefix=irc
time="2023-04-06T21:56:32Z" level=debug msg="handle girc.Event{Source:(*girc.Source)(0xc000c2e450), Tags:girc.Tags{"time":"2023-04-06T21:56:32.231Z"}, Timestamp:time.Date(2023, time.April, 6, 21, 56, 32, 231000000, time.Local), Command:"JOIN", Params:[]string{"#your-land-web", "*", "YL"}, Sensitive:false, Echo:false}" func=handleJoinPart file="bridge/irc/handlers.go:117" prefix=irc
time="2023-04-06T21:56:32Z" level=debug msg="333: Topic set by Alias [~Alias@user/alias] [2022-04-04 21:48:03 +0000 UTC]" func=handleTopicWhoTime file="bridge/irc/handlers.go:264" prefix=irc
time="2023-04-06T21:56:32Z" level=debug msg="handle girc.Event{Source:(*girc.Source)(0xc000a171a0), Tags:girc.Tags{"time":"2023-04-06T21:56:32.232Z"}, Timestamp:time.Date(2023, time.April, 6, 21, 56, 32, 232000000, time.Local), Command:"JOIN", Params:[]string{"#your-land-chat", "*", "YL"}, Sensitive:false, Echo:false}" func=handleJoinPart file="bridge/irc/handlers.go:117" prefix=irc
time="2023-04-06T21:56:32Z" level=debug msg="handle girc.Event{Source:(*girc.Source)(0xc000b40690), Tags:girc.Tags{"time":"2023-04-06T21:56:32.233Z"}, Timestamp:time.Date(2023, time.April, 6, 21, 56, 32, 233000000, time.Local), Command:"JOIN", Params:[]string{"#your-flight-chat", "*", "YL"}, Sensitive:false, Echo:false}" func=handleJoinPart file="bridge/irc/handlers.go:117" prefix=irc
time="2023-04-06T21:56:32Z" level=debug msg="handle girc.Event{Source:(*girc.Source)(0xc000b40f60), Tags:girc.Tags{"time":"2023-04-06T21:56:32.233Z"}, Timestamp:time.Date(2023, time.April, 6, 21, 56, 32, 233000000, time.Local), Command:"JOIN", Params:[]string{"#your-land-dev", "*", "YL"}, Sensitive:false, Echo:false}" func=handleJoinPart file="bridge/irc/handlers.go:117" prefix=irc
time="2023-04-06T21:56:32Z" level=debug msg=""CLIENT_INIT irc.chat.twitch.tv:6697"" func=handleOther file="bridge/irc/handlers.go:170" prefix=irc
time="2023-04-06T21:56:32Z" level=debug msg=""CLIENT_DISCONNECTED irc.chat.twitch.tv:6697"" func=handleOther file="bridge/irc/handlers.go:170" prefix=irc
time="2023-04-06T21:56:32Z" level=error msg="disconnect: error: EOF" func=doConnect file="bridge/irc/irc.go:194" prefix=irc
time="2023-04-06T21:56:32Z" level=fatal msg="Starting gateway failed: Bridge irc.tv failed to start: connection failed EOF" func=main file="matterbridge.go:66" prefix=main
This is the same log as in 42wim/matterbridge#2037 (comment)
Connection to twitch.tv successful
Run matterbridge 1.26.0 with the config mentioned above
linux/debian
Workaround available: Downgrade to matterbridge-1.25.2-linux-64bit: version: 1.25.2 42wim/matterbridge@20f841c or removal of commit 8240917
Not urgent
Selfcontrolled, we can do all kinds of tests with it
Touches #54
Blocks 42wim/matterbridge#2037
For my reference 4114
with this, we can potentially find lag. Client.Lag()
would be useful.
allow support for changing the frequency of this?
would require using net.Dialer{}.LocalAddr
. relates to #3.
First off, love girc. I have a ridiculous fork of it with a lot of wacky changes and have gotten pretty familiar with this masterpiece of yours.
Anyway, you're missing definition and handling of code 329 AKA RPL_CREATIONTIME. (details below)
<client> <channel> <creationtime>
Sent to a client to inform them of the creation time of a channel. is the name of the channel. is a unix timestamp representing when the channel was created on the network.
I almost made a PR to add it for you into the definitions. However, after a bit of searching I'm not so sure it's in the RFC(s). Because of this I wasn't sure where exactly you'd want to place it, if at all.
It is however, very prevalent and well-enough documented. It likely should be in constants, and if you're feeling cheeky enough, maybe even handled and stored in the channel state. (this is what I plan on doing with girc-atomic).
I haven't debugged it any further, but it seems SASL doesn't work on irc.rizon.net
I'm getting :irc.rizon.io 904 user :SASL authentication failed
Ref: IRCv3 WHOX
The
WHOX
extension allows clients to request additional fields in theWHO
response (e.g. account name) or omit default fields (e.g. username and hostname).
Once support is added, the IRCv3 Library Support page needs to be updated to include these features.
Any plans to implement this?
Running this on a test server:
:irc.foonet.com 255 r :I have 2 clients and 0 servers
:irc.foonet.com 265 r 2 2 :Current local users 2, max 2
:irc.foonet.com 266 r 2 2 :Current global users 2, max 2
:irc.foonet.com 375 r :- irc.foonet.com Message of the Day -
:irc.foonet.com 372 r :- 17/1/2019 0:18
:irc.foonet.com 372 r :- message
:irc.foonet.com 376 r :End of /MOTD command.
:r MODE r :+iwx
JOIN #inout
:[email protected] JOIN #inout * :r
WHO #inout %tacuhnr,1
MODE #inout
:irc.foonet.com 353 r = #inout :[email protected] @[email protected]
:irc.foonet.com 366 r #inout :End of /NAMES list.
:irc.foonet.com 352 r #inout r 381B2339.5C917854.9C80D685.IP irc.foonet.com r H :0 r
It tries to do a WHO asking for a variable name "WHO #inout %tacuhnr,1"
Look around line 170 in builtin.go but unsure how to fix it.
`
Ref: IRCv3 setname
Historically, a userβs realname could only be set on the initial connection handshake. However, multiple IRC servers have provided independent non-standard commands to update the realname without reconnecting. This specification describes a standardised behaviour based on these existing implementations.
Once support is added, the IRCv3 Library Support page needs to be updated to include these features.
hey!
I wrote some custom handlers for handling NICK
messages and it's related errors, but the internal conflict handlers interfere (resulting in silly things like the name being reset back), and due to the nature of this handler, there is state involved with figuring out a nick conflict (specifically, a number is incremented), meaning it cannot be easily translated to HandleNickCollide
.
for this reason I propose that NICK initialnick
optionally removed from the initial setup process, letting the user do it after a CONNECTED
event, but still keep the internal nick tracking
How about we add a struct field QuitMessage
to type Config
and use this quit message upon graceful exit?
Are there any ways to do this currently, as in quit with a specified quit message whenever (for example when handling signals)?
It appears that if a low-quality network connection (e.g. being behind a Whonix Tor transproxy) is being used, the execLoop
and/or sendLoop
goroutines aren't reliably being closed properly after a disconnect.
Originally reported at 42wim/matterbridge#1743 (detailed logs are in that issue) ; @42wim suggested that I report it here. I'm happy to help with trying to narrow down the bug, e.g. by collecting more detailed logs, but my ability to help may be limited since I'm not the author of the affected software. In any event please let me know what I can do to help get this fixed.
The documentation states the following:
// occurs when we're disconnected from the server (user-requested or not)
DISCONNECTED = "CLIENT_DISCONNECTED"
However, the code to invoke handlers registered for this constant seems to have been removed in 4951996. The DISCONNECTED
constant is currently not used anywhere in the code base.
Not sure if you simply forgot removing the constant in 4951996...
When using the client commands Part and PartMessage a JOIN will be sent instead of a PART.
client.Cmd.Part("#mychannel", "poof")
client.Cmd.PartMessage("#mychannel", "poof")
debug:19:37:52 conn.go:451: > JOIN #mychannel
debug:19:37:52 conn.go:451: > JOIN #mychannel :poof```
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.