Coder Social home page Coder Social logo

hupili / snsapi Goto Github PK

View Code? Open in Web Editor NEW
159.0 37.0 53.0 4.46 MB

Cross platform middleware for Social Networking Services: Twitter, Facebook, SinaWeibo, Renren, RSS, Email, Sqlite, ... (more coming)

Home Page: http://snsapi.ie.cuhk.edu.hk

Python 99.59% Shell 0.41%

snsapi's Introduction

NOTICE:

This projects is no longer maintained. However, the concept being pursued and the rationales are still valid. Would like to hear from anyone who works on similar stuff. Hope one day the Meta Social Network dream can come true. Let us decentralize teh social networking landscape!

The following list of resources may be useful to you: (PR to expand the list are highly welcome)

I am also available to talk. Feel free to dig out email from commit history, =D.


SNSAPI

A cross-platform middleware for Social Networking Services (SNS):

Lightning Demo 1 -- Read Twitter Timeline

Step 1.

Register user and developer on Twitter. Apply for application keys and access tokens.

Step 2.

Save the following codes to mytest.py in the root dir of this project:

from snscli import *
nc = new_channel('TwitterStatus')
nc['app_key'] = 'Your Consumer Key from dev.twitter.com'
nc['app_secret'] = 'Your Consumer Secret from dev.twitter.com'
nc['access_key'] = 'Your Access Token from dev.twitter.com'
nc['access_secret'] = 'Your Access Token Secret from dev.twitter.com'
add_channel(nc)
print home_timeline()

Filling your app credentials in the above script: app_key, app_secret, access_key, access_key.

Step 3.

Try it by python mytest.py. You will see your home timeline from twitter.

Remarks

SNSApi unifies the interfaces of all SNS such that retrieving new messages from all other platforms are the same:

  • Create a new channel configuration and add_channel it.
  • Invoke a single home_timeline() to obtain an aggregated timeline from all channels in a batch.

Lightning Demo 2 -- Backup Your Data

Step 1.

Configure a channel.json file with two channels:

  • One is called "myrenren" and it interfaces with Renren (an OSN in China).
  • The other is called "mysqlite" and it interfaces with a SQLite3 DB.

See one example channel.json configuration.

Step 2.

Save the following codes to backup.py in the root dir of this project:

from snsapi.snspocket import SNSPocket
sp = SNSPocket()
sp.load_config()
sp.auth()

ml = sp['myrenren'].home_timeline()
for m in ml:
    sp['mysqlite'].update(m)

Step 3.

Try it by python backup.py. Now your timeline of Renren (latest 20 messages by default) is backed up to the SQLite DB. You can run this script on a regular basis to backup data from all kinds of SNS.

Remarks

SNSApi unifies the data structures of all SNS so as to enable flexible/ programmable inter-operation between those services:

  • Backup one message in SQLite is just "update a status" there.
  • In order to read those messages, just invoke home_timeline of your SQLite channel.
  • The data in SQLite DB are ready for further analysis. For example, I remember someone said that "snsapi is awesome". Who posted it? I can not recall. Now, enter sqlite and use one line of command to get the answer: select * from message where text like '%snsapi%';.
  • You can also use EMail or RSS to distribute your statuses and follow the updates of your friends.
  • When there are new platforms, it's just one configuration away to use them. The intervention from app developer is not needed.

Lightning Demo 3 -- An Ad-Hoc DSN

Decentralized Social Network (DSN) is the next paradigm of social networking. Current centralized services have a lot of problems, e.g. Spying for free.

SNSApi is just a middleware to offload your burden in interfacing with different platforms. Now, try to build something without worrying about the interfacing detials.

See RSoc Community Page if you are interested.

Supported Platforms

Enther the interactive shell by python -i snscli.py. Get the supported platforms as follows:

Supported platforms:
   * DoubanFeed
   * Email
   * FacebookFeed
   * InstagramFeed
   * RSS
   * RSS2RW
   * RSSSummary
   * RenrenBlog
   * RenrenFeed
   * RenrenPhoto
   * RenrenShare
   * RenrenStatus
   * RenrenStatusDirect
   * SQLite
   * SinaWeiboBase
   * SinaWeiboStatus
   * SinaWeiboWapStatus
   * TencentWeiboStatus
   * TwitterStatus
   * ...

More platforms are coming! Please join us!

Get Started

  • Clone and install dependencies via pip. Then you are ready to go. See installation guide if you need more detailed information. See troubleshooting page if you encounter problems in your initial tests.
  • We have several demo apps in this repo. You can start with them and see how to use those classes of SNSAPI.
  • Users who don't want to write Python or other non-Python programmers can start with our command-line-interface (snscli.py). The official SNSAPI website should get your started quickly along this line. This CLI can allow interfacing with other languages using STDIN/ STDOUT.
  • Users who are not comfortable with CLI can use the graphical-user-interface (snsgui.py). See more user interfaces.

Resources

License

copyleft

All materials of this project are released to public domain, except for the followings:

  • snsapi/third/*: The third party modules. Please refer to their original LICENSE. We have pointers in snsapi/third/README.md for those third party modules.

Other

master dev Analytics

snsapi's People

Contributors

antsword avatar cllu avatar fqj1994 avatar guori12321 avatar hupili avatar hzhua avatar iptux avatar kelvin-zhong avatar uxian avatar wcyz666 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

snsapi's Issues

新浪审核问题?

同学你好 我按照你的那个10行代码写一个钟楼的日志一步一步做的,但是最后一步的时候显示的那个url无法打开,不知道是什么原因,另外在新浪微博上的那个应用信息中的应用地址该怎么填呢?

Code Stylization (PEP8)

PEP8 is widely adopted by the Python community. It's a good idea to follow.

The "line too long" warning is not very reasonable since developing environments are wider nowadays. Also, some lines contain URL templating, making it such long. Other suggestions are reasonably to follow.

pep8 can be installed via pip

find -name "*.py" | xargs pep8 | grep -v E501

Support Parameterized Message Parsing

Current way of Message parsing:

  • Platform class invoke self.Message, which is assigned as a special message class derived from Message.
  • If you want to customize the behaviour of Message passing, you can derive the platform and its corresponding message class.

Here is a use case where the above way is not enough. RSS feeds are too heterogeneous. Some of the feeds do not include timezone suffix. In order to parse a correct time, we must add this suffix according to user's knowledge. In current implementation, platform class and message class are isolated. There is no way to pass this information.

A demo of word translation

We can write a demo just like Xiaohuangji. If some one "at" the robot and send an english word, the robot can reply the Chinese meaning with the help of third party online dictionary who have an open API. It should follow these steps:

  1. Checking if someone at me,
  2. Checking if it is a word
  3. Translating the word with third party API
  4. Reply

Because I just touch this project, I don't know how to realize the first step with snsapi.
Could someone help me?

Renren API Develop

A thread to record renren api developments. Notice of discrepancies to other APIs components.

File encoding

Should we shift the whole project to utf-8 encoded? Since we do not write Chinese to .py and .md, it seems doesn't matter.
I'd like to confirm that, # -- coding: utf-8 -- in the head of file only means the encoding format is utf-8 when executing program, that means strings are treated as utf-8 format by default, but the .py files stays ansi(gbk for Chinese Win) encoded laying in Windows's file system. Is that correct?

Not even a PS. PS. F**k windows, I want to have a Mac, I hear this from my heart, but I need to wait until the one in hand become old enough :(

Timeout handle in HTTP request

Platform plugins are recommended to use SNSBase._http_get or SNSBase._http_post as the interface to remote servers. We should add timeout in the two methods.

The current application model of SNSAPI is synchronized invocation. Without timeout, one platform stuck can cause whole application stuck. e.g. Tencent server can not be reached from HK recently.

Rename QQ to Tencent Weibo

I think that it is more appropriate to call the microblog service provided by Tencent to Tencent Weibo. Actually you can switch to English version on http://t.qq.com and the logo is just Tencent Weibo. Similarly, the Sina one should be named as Sina Weibo.

Pickling Message Class Failed

This problem roots from the second round restructuring in Oct. It exists till commit defbb94

The original purpose for nested Message class inside platform class is for better organization. It turns out the pickle can only serialize top-level class in a module. There are some workarounds but they look too tricky to be in SNSAPI.

People are arguing that class nesting should be used only when necessary.

In our case, nesting help to unify the name. Say, one only need to know self.Message is the message class for current platform and do not worry about different naming. This is essential in platform subclassing. You may have subclass a platform because some methods there will be reused. However, the platform identifier in the Message object are different. If the base class refer only to self.Message in its methods, things are easier.

Reference:

Error Handling: Absorb or Raise?

This is a discussion of design philosophy. Different programmers may have different opinions and the common practices in different languages are also different.

I raise the question here to expect some guide lines form Python experts.

  • Absorb. Just as I did after the Oct restructuring. In the development of SNSRouter, there is a backend thread to read messages and a frontend thread to handle HTTP request. Errors are raised from different platforms in different cases. I feel annoying and added many failure tolerance codes: absorb the exception and just leave a log entry there.
  • Raise. Just let the code raise the error. In this case, higher level module should look into different errors and determine what to do.

Maybe a better way is to absorb underlying error (e.g. from "feedparser"), log them, and raise a new snsapi defined error.

Initial Server Deployment Test

I finished one test deployment on our SD server:

  • Add mapping to hosts file:
118.145.12.28 copy.the.code.to.client
  • Visit http://118.145.12.28
  • The default script will redirect you to the authentication url.
  • After authentication, Sina redirects you to 'copy.the.code.to.client', which is mapped to our server.
  • The server invoke home_timeline() and respond the output.
  • Token is not saved for the moment.

The web document root is:

/home/shared/www

CGI handler is added for '.cgi', '.pl', '.py'.

You can test it by visiting the IP. 'url_routing.pl' is the entry of all logic. You can follow it and find how it works. A temporarily modified version of snsapi is in:

/home/shared/snsapi

Now the service deployment is a kludge. Just for demonstration. There are some upgrade points for snsapi:

  • auth() should be divided into two stages. The first stage generate authentication url and request it. By "request", it print it to stdout, invoke default browser, or use wget, etc. The second stage receives authenticated code, and finish the rest. By "receive", it gets the code through web server, console input, etc. HTTP is stateless. Dividing into two stages can make the authenticate process stateless. Or, we have to run the snsapi at background, and wait for the next HTTP request(after authentication) from the user. See the following for a demo:
snsapi/snsapi.py
_first.py
_second.py
  • UTF8 encoding. I come across the following errors in the deployment.
Traceback (most recent call last):
  File "second.py", line 3, in <module>

import snsapi
  File "/data/shared/snsapi/src/snsapi/__init__.py", line 2, in <module>

from plugin import *
  File "
/data/shared/snsapi/src/snsapi/plugin/sina.py
", line 
43

If I directly invoke the python script from console, everything is OK. When the script is invoked by 'url_routing.pl' using the "``" notation, it prompts this error. I think the executing environment is different. I tried to switch users, export "$LANG" env var. All is in vain. Before we find the real problem, a temporary fix is:

print "[%s] at %s \n  %s" % (self.username.encode('utf-8'), self.created_at.encode('utf-8'), self.text.encode('utf-8'))

Specialized Forwarding Functions for OSNs

Enable intra-site specialised forwarding. Currently, we only have a general forwarding function in snsbase using update methods of each platform. Specialized forwarding can help retain more information.

Redirect from #26

new_channel() upgrade of SNSPocket class

Current new_channel() returns a general json object for all platforms.

  • The parameter-less call to this function should return a maximal configuration set.
  • It should allow specify a certain platform. Then a platform specific json object is returned, which tells the user what are the essential fields of this platform.

This upgrade can make the execution flow more self-contained. Or, users should have background knowledge of what fields are required by what platforms.

Asynchronous Message Queue

With the increasing number of channels, synchronous call to all methods causes larger and larger delay. Before it exceeds human tolerable span, we should consider an asynchronous message queue.

The queue:

  • Is built on SNSPocket to avoid platform specific details.
  • Support multiple threads subject to configuration.
  • Support per-platform / per-account / per-key locking. e.g. simultaneous request to a single platform from a single computer may fail.
  • Act as a persistent data structure. e.g. dump to SQLite up receiving new message.
  • Can be operated as a channel. This allows hierarchical organisation of SNS.

Requires experienced Python developer and one who is familiar with SNSAPI framework.

Welcome any prototyping and restructuring effort to make the queue easy to implement.

One Import Trick

See this post

This trick may be useful to reduce those repeated codes in our project:

  • Multiple appearances of try import xxjson except xxx import yyy json...
  • Those if __name__ == 'main' import xxx else import yyy patterns at the beginning of plugin files, e.g. sina.py.

TimeZone Failure

@uxian I move the bug to SNSAPI for further investigation.

report a tiny bug, in Windows, the following third method is not work, but the second one (without sharp) works good.

in snsapi/utils.py #line 135

def utc2str(u):
    #return str(datetime.datetime.fromtimestamp(u))
    return _format_date(datetime.datetime.utcfromtimestamp(u))
    #return _format_date(datetime.datetime.fromtimestamp(u, tz.tzlocal()))

Following lines are trackback. you catched this Exception before, I just commented out the "try/catch" to see why I got 0 new messages when I open 127.0.0.1/home_timeline.

  File "snsapi\snsapi\utils.py", line 138, in utc2str
    return _format_date(datetime.datetime.fromtimestamp(u, tz.tzlocal()))
  File "C:\Python27\lib\site-packages\dateutil\tz.py", line 92, in utcoffset
    if self._isdst(dt):
  File "C:\Python27\lib\site-packages\dateutil\tz.py", line 135, in _isdst
    return time.localtime(timestamp+time.timezone).tm_isdst
ValueError: (22, 'Invalid argument')

The Base64 Encoding and Decoding of "access_token"

Can we retire the operation?

It seems the encoding and decoding process is redundant. As long as we open the code, one know how to decrypt it. I think the security lies on the protection of the saved tokens. This is the job upper layer developer/user. We let them know there are something important saved. One can choose not to save the tokens for security. Or one can choose a "safe" place through configuration.

The app_key Upgrade Application on Sina (following discussion of key kill actions of some service providers)

The two reviews of Sina:

  • app review.
  • square review.

@uxian
Which one can extend the token validation to 7 days?

I'm also thinking about hiding the secret key. It's like a mirror problem of Issue9... As long as the secret is in the binary, no matter how hard it is coded, there are still ways to get it. I think their APIs are intended for web use at the beginning. The secret key is secured on the developers own server. End users don't have access to the logics in the background.

How about this:

  • We release snsapi, some sample app codes(e.g. forwarder).
  • We don't release a complete app solution for any platforms.
  • Users(of forwarder) apply their own keys and configure it accordingly. We are assuming higher level users.
  • The upgrade of app_key on Sina is only to do us a favour. We are the only two users corresponding to "snsapi+your sina key". The ultimate situation should be one person one key.

If our release is snsapi with keys of different platforms in a bundle, I think it's better to put those keys on our server rather than code them in the client. Our server will act as a proxy for any transaction involving secret key:

  • Sina/QQ. Only auth stage is related with secret key. We only need to develop an authentication bouncer and store the access_token in user's local storage.
  • Renren. The second stage of auth and successive request are all related with secret key(they need a signature). More coding is needed.

class Message comparable by time

if more than one channel is available, home_timeline(count) return a MessageList contains count * channel_number Messages, but that intent to be exactly count Messages only.

MessageList should be sorted first and return the first count Messages

this requires Message class comparable by time, more recently Status should show first

Start a local server to accept authorization code

Currently you need to copy and paste the returned code when do authorization, which is tedious. Since this is mainly a local application, you may consider starting a webserver on localhost (which is provided in the standard SimpleHTTPServer module) and point the callback URL to localhost, then you can get the code without human intervention. This is what they do in google-api-python-client.

Milestone Review and Code Cleaning

We are finishing the 1st mile after sina app_key upgrade. Here are some code cleaning points I can think of:

  • The homepage. There are some non-universal files, which will be a distraction for new comers. I'm afraid if I remove them directly, you will lose them from the local storage on next checkout. Can you upgrade the gitignore and clean them.
  • Also some irrelevant arguments in homepage README. After the dir tree cleaning, they can be retired in the historical commitments.:)
  • "#" comments in the project. There are three types of comments in general. 1) real comments, explain the rationales, frame the code into sections, etc; 2) option comments, like those in snsconf.py, not to explain things, just to notify the users some possibilities; 3) debugging comments, just for fast code recovery in development process. After getting a stable version, the 3)-rd type can be removed. (Git will remember them for us)
  • Many TODOs... Some have already been done. For those are not, we need to work them into wiki or issue system to track the process. (can first temporarily note them in this thread for discussion)
  • For any other doubts in the code(no matter how small), it's also the proper time to note them.

@uxian
Can you take over some of them?

Just note the part we are working on in this thread, in order to avoid repeated labour.

SQLite Channel

Enable simple SQLite storage is easy. What we want is a unified schema there. Say can store raw json associate with each status for future retrieval; store common fields in a flat manner, e.g. platform, status_id, time, content, etc.

This depends on the Restructure of status class. Should come after Issue #14.

A Demo App -- Deleted Message

If one message is deleted by the hosting platform, that message is probably an informative one!

Using snsapi, one can collect all the home_timeline continuously. If it spots any deleted message, add it to a dedicated web page (or RSS feeds). This application will be attractive.

Refresh Token ([OT]--a demo followed)

I just checked the data structure returned by QQ and Renren (*.save files). The two platforms have refresh_token in it. It's better to refresh the access_key when it's near timeout. Then we don't have to prompt user's browser every time. I'm not sure whether the SP really implement the function of refresh. Check it out later.

Sina is the big brother... One day timeout; no refresh_token returned....

@uxian

最后一步出现的问题

Auth second fail. Catch exception: HTTP Error 401: Unauthorized
这个是把那个url复制到终端里面 ,出现的一些提示,好像没有得到授权...请问如何解决呢?
完整信息 [WARNING][20130116-124918][snsbase.py][_oauth2_second][178]Auth second fail. Catch exception: HTTP Error 401: Unauthorized

SNSPocket

The prototyping of container class is done:

https://github.com/uxian/snsapi/compare/3120e7fd29d32c727e642fc43601f3160d9fbad7...dfa076e167171873697873aff72877fc15132c18

Users can base their work on different layers:

  • Plugin: to operate one type of channel specifically.
  • SNSAPI: for unified interface. (method routing should be done later in this layer)
  • SNSPocket: for batch operation. This is intended for APP developer. Before SNSPocket, app developer should worry about load configs(like channel.conf). Once there are new fields in the config, they should also pay attention. SNSPocket will cover those details.

Now the test scripts are simplified.

Some enhancements on Snsbase class (add HTTP request header)

When I tried to implement Github api, I found Github didn't support any query string to tell the github show json response...
If you want to get json response , what only you can do is to change header.
Github auth Doc

However, Snsbase _http_post(self, baseurl, params) doesn't support add_header (bcz using urllib not urllib2)

So I have to override _http_post,import urllib2 , I think we can enhancement snsbase class by using urllib2 and change _http_post , adding a parameter headers

btw:github api is going to be finished. ^_^

Enhancement of OSN Platforms

  • Enable intra-site specialised forwarding. Current dev branch only has general forwarding, which causes drastical information loss.
  • For Sina and Tecent microblog messages, parse to a finer grain. e.g. if it is a picture, also parse the link in order for upper layer services to determine what to forward. Microblog is limited in text length. Link with a little description is the current trend.
  • Reverse lookup of URL abbreviation service.
  • In the _cat function, abbreviate URL first.

Restructure Status Class / Config Class

In the development of apps, pocket and cli, I find the current way of handling Status and Configs are not flexible.

Here's the proposal for Status class:

  • Base on dict. Current attributes are stored in dict.
  • Convert any field into: dict, list, unicode. (making sure it is json serialisable)
  • Three levels of serialization:
    • Console output. through "str", "unicode". Only serialize the basic information, conforming to status output convention. This is intended for end user operating in console. Currently in use.
    • Interface output. Detailed and structured output. The format should be easy to parse since it is intended to other programmes waiting at the other end of the pipeline. All SNSAPI information is formatted (e.g. besides basic information shown on console, ID information is also there in case one want to reference a Status Object in another call). More details are to be designed. Different formats should be supported (driven by app demand).
    • Complete output. Sometimes, presented message is calculated from raw response json object. e.g. in the RSS channel, there is no the notion of "status" nor equivalence. We put author, title, link, date together to construct a "text" field so that it is compatible with all other components. Complete output will be the last source of all information. Make it there in case some application use it.

Configs (for each channel) has similar problem. Better to substitute

c.platform = xxxx

to

c.jsonconf["platform"]

This simplifies the config loading and saving process. Also easier to integrate crypto utility (easier to serialize). Or in the place we need serialization, one should do several:

s["xxxx"] = c.xxxx
s["yyyy"] = c.yyyy
...

Forwarder (A Demonstration App)

Following is the design. Welcome any feedbacks.

Configurations

Every configuration is in json.

  • channel.json : example already in 'src/conf'; stick to flat style for the moment(i.e. duplicate app_key, app_secret for each channel).
  • forwarder.json: configures a list of "in_channels" and a list of "out_channels"

Logic

  • Read home_timeline() from all in channels.
  • Write all out channels using update().
  • Local storage is required to guarantee no duplicate updates.

I'll also use json to store the updated messages(as a lightweight DB).
It should work well with small amount of messages.

Test

I will deploy it on a server and use cron job to run the forwarder.
As a first test, I'll subscribe to the RSS of this repo(wiki pages, etc), and forward news to Sina or QQ.

Note that channels are transparent to forwarder, so SNS 2 SNS forwarding is also available subject to proper configurations.

Intensive Restrucutre Notes

Record restructures in milestone 3. Branch is "dev".

After restructure "test/" and "app/" may become invalid. Call for more test.

@uxian

Extend The Lifetime of Authorization

To deploy the first instance of forwarder, we need to extend the lifetime of authorisation. Or it will annoying to authorise in browser and copy the code everyday~~~~

Here's the plan:

  • Set the callback URL to my app server, which will then automatically receive the code(or full callback URL).
  • Login weibo.com and export cookies. Upload the cookies to app server.
  • Use wget with the same configuration of login browser, load the cookies. This will trigger authentication and the code is sent to the app server.

File level synchronisation is needed. This should extend lifetime of auto authorisation to at least the cookie timeout period.

URL Shortening and Expanding Services

We need this in our utility collection.

  • Expand url if it is from URL shortening services. This help downflow modules to make better decision.
  • Shorten the URLs contained in a message before posting (forwarding).
  • Some URLs are shortened several times. Recursive expanding is needed.

Towards Longer Authenticated Period

Is there any options in the OAuth to request a longer authenticated period?

Currently, Sina seems to only grant 1 day access privilege, which is a bit short. QQ grants as long as one week.

Another option is to enable basic authentication. The user can choose to write his account in the configuration file. In this way, the app can login at any time without human operation in the browser.

a mock issue

build snsapi, several usable plugin, test app.

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.