Giter Club home page Giter Club logo

weapp's Introduction

微信小程序服务端 SDK (for Golang)

说明

  • v1 版本入口
  • v2 版本入口
  • 查看完整文档
  • SDK 暂不包含支付相关内容 已有很多优秀的支付相关模块;
  • 微信小程序的功能和接口一直在持续更新迭代,如果遇到没有的接口或者不符合当前实际情况的接口请提交 issue 或者发起 pull request;

获取代码

go get -u github.com/medivhzhan/weapp/v3

初始化

  • 初始化 SDK
package main

import (
 "github.com/medivhzhan/weapp/v3"
)

func main() {
 sdk := weapp.NewClient("your-appid", "your-secret")
}
  • 自定义 HTTP 客户端
package main

import (
 "crypto/tls"
 "net/http"
 "time"

 "github.com/medivhzhan/weapp/v3"
)

func main() {
 cli := &http.Client{
  Timeout: 10 * time.Second,
  Transport: &http.Transport{
   // 跳过安全校验
   TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
  },
 }

 sdk := weapp.NewClient(
  "your-appid",
  "your-secret",
  weapp.WithHttpClient(cli),
 )
}
  • 自定义日志
package main

import (
 "log"
 "os"

 "github.com/medivhzhan/weapp/v3"
 "github.com/medivhzhan/weapp/v3/logger"
)

func main() {
 lgr := logger.NewLogger(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Info, true)

 sdk := weapp.NewClient(
  "your-appid",
  "your-secret",
  weapp.WithLogger(lgr),
 )

 // 任意切换日志等级
 sdk.SetLogLevel(logger.Silent)
}
  • 自定义缓存
package main

import (
 "time"

 "github.com/medivhzhan/weapp/v3"
)

type MyCache struct{}

func (cc *MyCache) Set(key string, val interface{}, timeout time.Duration) {
 // ...
}

func (cc *MyCache) Get(key string) (interface{}, bool) {
 return "your-access-token", true
}

func main() {
 cc := new(MyCache)

 sdk := weapp.NewClient(
  "your-appid",
  "your-secret",
  weapp.WithCache(cc),
 )
}
  • 自定义 token 获取方法
package main

import (
 "github.com/medivhzhan/weapp/v3"
)

func main() {
 tokenGetter := func() (token string, expireIn uint) {

  expireIn = 1000
  token = "your-custom-token"

  return token, expireIn
 }

 sdk := weapp.NewClient(
  "your-appid",
  "your-secret",
  weapp.WithAccessTokenSetter(tokenGetter),
 )
}

调用接口示例

官方文档

package main

import (
 "fmt"
 "log"

 "github.com/medivhzhan/weapp/v3"
 "github.com/medivhzhan/weapp/v3/auth"
)

func main() {
 sdk := weapp.NewClient("your-appid", "your-secret")

 cli := sdk.NewAuth()

    // 用户支付完成后获取该用户的 UnionId
 rsp, err := cli.GetPaidUnionId(&auth.GetPaidUnionIdRequest{})
 if err != nil {
  log.Fatal(err)
 }

    // 检查加密信息是否由微信生成
 rsp, err := cli.CheckEncryptedData(&auth.CheckEncryptedDataRequest{})
 if err != nil {
  log.Fatal(err)
 }

    // 登录凭证校验
 rsp, err := cli.Code2Session(&auth.Code2SessionRequest{})
 if err != nil {
  log.Fatal(err)
 }

    // 获取小程序全局唯一后台接口调用凭据
 rsp, err := cli.GetAccessToken(&auth.GetAccessTokenRequest{})
 if err != nil {
  log.Fatal(err)
 }

    // 检查微信是否返回错误
 if err := rsp.GetResponseError(); err != nil {
  log.Println(err)
 }

 fmt.Println(rsp)
}

接收微信通知

官方文档

package main

import (
 "log"
 "net/http"

 "github.com/medivhzhan/weapp/v3"
 "github.com/medivhzhan/weapp/v3/server"
)

func main() {
 sdk := weapp.NewClient("your-appid", "your-secret")

 //  通用处理器
 handler := func(req map[string]interface{}) map[string]interface{} {
  switch req["MsgType"] {
  case "text":
   // Do something cool ...
  }

  return nil
 }

    // HTTP handler
 http.HandleFunc("/wechat/notify", func(w http.ResponseWriter, r *http.Request) {
  srv, err := sdk.NewServer("token", "aesKey", "mchID", "apiKey", false, handler)
  if err != nil {
   log.Fatalf("init server error: %s", err)
  }

  // 调用事件处理器后 通用处理器不再处理该事件
  srv.OnCustomerServiceTextMessage(func(tmr *server.TextMessageResult) *server.TransferCustomerMessage {

   return &server.TransferCustomerMessage{}
  })

  if err := srv.Serve(w, r); err != nil {
   log.Fatalf("serving error: %s", err)
  }
 })
}

weapp's People

Contributors

faddei avatar fanchenggang avatar fyyang avatar honkiko avatar hubertzhang avatar ihipop avatar kingway126 avatar laeo avatar lixh00 avatar lwabish avatar royalmorty avatar royalrick avatar testwill avatar yizhi996 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

weapp's Issues

解密时报slice bounds out of range

I0918 09:11:59.652611 6 AuthUser.go:150] DecryptPhoneNumber params weAppSessionKey: DhdCKRNjmngLstiaaVQR6A== , encryptedPhone: h3NDDnf39Z76MgcWrgNzisq04RXWaF8Yr+3x+dRPbcn1v5akN71LyRlh9CzM2qHlSMEYiuLSNnHfdoZowcQmrJrK1b/15NUaO4u2dS0JBogKhoL3DPc+If+dXRnX8stCbJR6YvR4Md5hu2n/Oycf3nU/VwI5CmXL9um8g+66tUGJzQRhzDD2uHAjlZLpz1r2lZkUcRr56YRfq6/92FLA5w== ,ivPhone: LXxdk1o3Oa0N0+XgZvAUDg==
�[31m2018/09/18 09:11:59 [Recovery] 2018/09/18 - 09:11:59 panic recovered:
Host: aws-serverless-go-api.com
09:11:59
Accept: /
Accept-Encoding: gzip, deflate
Accept-Language: zh-cn
Content-Type: application/json
Referer: https://servicewechat.com/wx1c44031bd4ac4f81/0/page-frame.html
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Mobile/14F89 MicroMessenger/6.7.2 NetType/WIFI Language/zh_CN
X-Amzn-Trace-Id: Root=1-5ba0c15f-da768fce121aab102b9eb018
X-Forwarded-For: 27.154.25.254
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Golambdaproxy-Apigw-Context: {"accountId":"638953167227","resourceId":"cr4s8x","stage":"qa","requestId":"e5f8cd0a-bb22-11e8-b4bb-4b38a3060d33","identity":{"cognitoIdentityPoolId":"","accountId":"","cognitoIdentityId":"","caller":"","apiKey":"","sourceIp":"27.154.25.254","cognitoAuthenticationType":"","cognitoAuthenticationProvider":"","userArn":"","userAgent":"Mozilla/5.0 (iPhone; CPU iPhone OS
X-Golambdaproxy-Apigw-Stagevars: null
runtime error: slice bounds out of range
/usr/local/go/src/runtime/panic.go:502 (0x42ae08)
/usr/local/go/src/runtime/panic.go:35 (0x429cbd)
/go/src/vendor/github.com/medivhzhan/weapp/util/crypto.go:56 (0x93d510)
/go/src/vendor/github.com/medivhzhan/weapp/util/crypto.go:123 (0x93d510)
/go/src/vendor/github.com/medivhzhan/weapp/weapp.go:118 (0x93dab6)

更新到v2出错

go get github.com/medivhzhan/weapp@v2: no matching versions for query "v2"

isv接口调用

求问可以把token相关的cache公开或者增加一个set的接口吗?比如第三方平台开发小程序获取token的方式不太一样,可能需要手动进行一下设置。。

用beego 架构写的微信支付回调怎么接受不到数据

// PayCallback ...
// @title PayCallback
// @description 微信支付回调接口
// @router /pay_callback [get]
func (c *WechatPayController) PayCallback() {
// 必须在下单时指定的 notify_url 的路由处理器下
err := payment.HandlePaidNotify(c.Ctx.ResponseWriter, c.Ctx.Request, func(ntf payment.PaidNotify) (bool, string) { return true, "" })
logs.Error("pay_callback error:", err)
...
}

新模板消息集成

最近微信升级了模板消息功能,什么时候能集成下呢?

`// Package template 模版消息
package subscribe

import (
// "fmt"
"encoding/json"
"errors"
"net/http"
"strings"

"github.com/medivhzhan/weapp"
"github.com/medivhzhan/weapp/util"

)

const (
sendAPI = "/cgi-bin/message/subscribe/send"
)

// Message 模版消息体
type Message map[string]interface{}

// Send 发送模板消息
//
// @openid 接收者(用户)的 openid
// @template 所需下发的模板消息的id
// @page 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。
// @formid 表单提交场景下,为 submit 事件带上的 formId;支付场景下,为本次支付的 prepay_id
// @DaTa 模板内容,不填则下发空模板
// @emphasisKeyword 模板需要放大的关键词,不填则默认无放大
func Send(openid, template, page string, data Message,token string) error {
api, err := util.TokenAPI(weapp.BaseURL+sendAPI, token)
if err != nil {
return err
}

for key := range data {
	data[key] = Message{"value": data[key]}
}

body := map[string]interface{}{
	"touser":           openid,
	"template_id":      template,
	"page":             page,
	"data":             data,
}

payload, err := json.Marshal(body)
if err != nil {
	return err
}

res, err := http.Post(api, "application/json", strings.NewReader(string(payload)))
if err != nil {
	return err
}
defer res.Body.Close()

if res.StatusCode != 200 {
	err = errors.New(weapp.WeChatServerError)
	return err
}

var resp weapp.Response
if err = json.NewDecoder(res.Body).Decode(&resp); err != nil {
	return err
}
// fmt.Println("订阅消息:",api,string(payload),resp)
if resp.Errcode != 0 {
	return errors.New(resp.Errmsg)
}

return nil

}
`

希望可以增加第三方平台相关接口

现在开发SaaS系统的公司越来越多,对第三方平台接口的需要也越来越大,希望给予考虑。
非常感谢作者提供这样优秀的库,我们已经在项目中使用了。

最近有更新计划?

请问最近有更新计划?小程序项目准备从PHP转GO,期待更加完善的SDK,类似PHP的Easywechat...

weapp.DecryptMobile : invalid character '¼' looking for beginning of value

var (
req model_wechat.AuthReq
)

if err := c.ShouldBindJSON(&req); err != nil {
	response.Error(c, 500, nil, "c.ShouldBindJSON:"+err.Error())
	return
}

resp, err := weapp.Login(config.WechatConfig.Appid, config.WechatConfig.Secret, req.Code)
if err != nil {
	response.Error(c, 500, nil, "weapp.Login:"+err.Error())
	return
}
if err := resp.GetResponseError(); err != nil {
	response.Error(c, 500, nil, "weapp.Login resp.GetResponseError:"+err.Error())
	return
}

//处理用户信息
raw := sha1.Sum([]byte(req.UserInfo.RawData + resp.SessionKey))
wxUserInfo, err := weapp.DecryptUserInfo(resp.SessionKey, req.UserInfo.RawData, req.UserInfo.EncryptedData, hex.EncodeToString(raw[:]), req.UserInfo.IV)
if err != nil {
	response.Error(c, 500, nil, "weapp.DecryptUserInfo:"+err.Error())
	return
}

//处理用户手机号信息
var (
	mobile *weapp.Mobile
)
//如果手机信息不为空
if !reflect.DeepEqual(req.PhoneInfo, model_wechat.PhoneInfo{}) {
	mobile, err = weapp.DecryptMobile(resp.SessionKey, req.PhoneInfo.EncryptedData, req.PhoneInfo.Iv)
	if err != nil {
		response.Error(c, 500, nil, "weapp.DecryptMobile:"+err.Error()) //这里会报错 weapp.DecryptMobile:invalid character '¼' looking for beginning of value 每次character会变化,随机性的出现
		return
	}
}

开放decryptUserData方法

decryptUserData方法不一定用于封装的四个方法(DecryptMobile、DecryptShareInfo、DecryptUserInfo、DecryptRunData),还有其他的用途

需要支持 转发客服消息 transfer_customer_service

server.go line 321

switch res.MsgType {
	case MsgText:
		msg := new(TextMessageResult)
		if err := unmarshal(raw, tp, msg); err != nil {
			return nil, err
		}
		if srv.textMessageHandler != nil {
			srv.textMessageHandler(msg)
		}

	case MsgImg:
		msg := new(ImageMessageResult)
		if err := unmarshal(raw, tp, msg); err != nil {
			return nil, err
		}
		if srv.imageMessageHandler != nil {
			srv.imageMessageHandler(msg)
		}

	case MsgCard:
		msg := new(CardMessageResult)
		if err := unmarshal(raw, tp, msg); err != nil {
			return nil, err
		}
		if srv.cardMessageHandler != nil {
			srv.cardMessageHandler(msg)
		}

希望添加返回值,将客服消息转发至网页版客服

源文档:
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/customer-message/trans.html

退款接口返回"EOF"错误

image

将此处生成的xml数据(即body),以及自己的双向证书,用js的http客户端(因为js的http客户端是直接支持带双向证书的post请求的)去验证,能够退款成功。

说明xml数据是没问题的,应该是NewTLSClient或cli.Post请求导致的该问题。

生成小程序码会转义特殊字符

刚发现生成的小程序码在开发者工具中打开,URL中的 & 字符被转义成 \u0026 了,搜了下资料,json 包会默认转义特殊字符。

解密手机号接口报错

操作系统:win10
golang版本:1.14.1

正确传入 sessionkey , encrypted_data,iv 返回以下内容

invalid character '¶' looking for beginning of value

微信退款有点小问题

微信退款时,在校验微信订单号和商户订单号是否传了其中一个时,却使用了商户退款单号进行判断

wrong param with GetParams function

// 获取小程序前点调用支付接口所需参数 params, err := payment.GetParams(res.AppID, res.MchID, res.NonceStr, res.PrePayID, time.Now()) if err != nil { // handle error return }

the paySign should use apikey not MchID, and here is the wechat app pay document

缓存时间不对

func (c *memory) Set(key string, val interface{}, timeout time.Duration) {
	timer := time.AfterFunc(timeout, func() {
		_ = c.Delete(key)

	})

	item := memoryItem{val, timer}

	c.store.Store(key, item)
}

const time.Second time.Duration = 1000000000 // 1s

这里的timeout 应该是 timeout*time.Second

SubscribeMessage 一些错误

// SubscribeMessage 订阅消息
type SubscribeMessage struct {
	ToUser           string           `json:"touser"`
	TemplateID       string           `json:"template_id"`
	Page             string           `json:"page,omitempty"`
	MiniprogramState MiniprogramState `json:"miniprogram_state,omitempty"`
	Data             string           `json:"data"`
}

这里的Data是string 但是下面的是SubscribeMessageData?

sender := weapp.SubscribeMessage{
    ToUser:     mpOpenID,
    TemplateID: "template-id",
    Page:       "mock/page/path",
    MiniprogramState:cli.MiniprogramStateDeveloper, // 或者: "developer"
    Data:cli.SubscribeMessageData{
        "first-key": {
            Value: "value",
        },
        "second-key": {
            Value: "value",
        },
    },
}

cli.MiniprogramStateDeveloper 这个引用的路径不正确 这里的是weapp.MiniprogramStateDeveloper

解密手机号报错

每次小程序用户提交解密数据后 后台解析报这个错 每次 'x1a'这个地方会有变化. invalid character '\x1a' looking for beginning of value

用户登录时也有可能返回unionid

你好,
当用户登录方法Login时, 小程序若绑定了开放平台, 应该还需要返回unionid字段.
可否Login时也返回这个? 或者说Login时就返回struct.
谢啦

支付通知处理函数可能存在泄漏

您好,我在使用支付相关接口时,发现该处理函数缺少资源释放操作,还请确认下。

// HandlePaidNotify 处理支付结果通知
func HandlePaidNotify(res http.ResponseWriter, req *http.Request, fuck func(PaidNotify) (bool, string)) error {
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return err
	}

	var ntf paidNotify
	if err := xml.Unmarshal(body, &ntf); err != nil {
		return err
	}

	if err := ntf.Check(); err != nil {
		return err
	}

	replay := newReplay(fuck(ntf.PaidNotify))

	b, err := xml.Marshal(replay)
	if err != nil {
		return err
	}

	res.WriteHeader(http.StatusOK)
	_, err = res.Write(b)

	return err
}

模板消息体拼写错误

路径:weapp/message/template/template.go

// Mesage 模版消息体
type Mesage map[string]interface{}

Mesage应为Message

conbineURI 死循环了

版本: v3.6.0

package main

import (
	"fmt"
	"log"

	"github.com/medivhzhan/weapp/v3"
	"github.com/medivhzhan/weapp/v3/auth"
)

func main() {
	sdk := weapp.NewClient("your-appid", "your-secret")

	cli := sdk.NewAuth()

    // 登录凭证校验
	rsp, err := cli.Code2Session(&auth.Code2SessionRequest{})
	if err != nil {
		log.Fatal(err)
	}

    // 检查微信是否返回错误
	if err := rsp.GetResponseError(); err != nil {
		log.Println(err)
	}

	fmt.Println(rsp)
}

Code2Session 中调用 conbineURI,conbineURI 中调用 AccessToken,AccessToken 中又调用了 conbineURI (当 accessTokenGetter == nil 时)

3.6.1 无限获取 access token 死循环

昨天从3.4.0升级到3.6.1,我只自定义了缓存,没自定义获取token的方法,结果当调用一个需要token的方法时就会死循环无限获取token。

赶紧回退了,刚看了看最近的改动,token部分确实有不少,还没来得及仔细看问题出在哪。

订阅消息有单条的,不全都是数组

{
"ToUserName": "gh_123456789abc",
"FromUserName": "o7esq5PHRGBQYmeNyfG064wEFVpQ",
"CreateTime": "1620963428",
"MsgType": "event",
"Event": "subscribe_msg_sent_event",
"SubscribeMsgSentEvent": {
"List": {
"TemplateId": "BEwX0BO-T3MqK3Uc5oTU3CGBqzjpndk2jzUf7VfExd8",
"MsgID": "1864323726461255680",
"ErrorCode": "0",
"ErrorStatus": "success"
}
}
}

然后源码里面是绑定一个数组,真的是伤不起

GET不响应

package main

import (
	"fmt"
	"net/http"

	"github.com/medivhzhan/weapp/v3/server"
)

func main() {

	http.HandleFunc("/sub", func(w http.ResponseWriter, r *http.Request) {
		srv, err := server.NewServer("appid", "", "", "mchID", "apiKey", false, nil)
		if err != nil {
			fmt.Printf("init server error: %s", err)
		}

		// 当用户触发订阅消息弹框后
		srv.OnSubscribeMsgPopup(func(msg *server.SubscribeMsgPopupEvent) {

			// Do something cool ...

		})

		// 当用户通过设置界面改变订阅消息事件内容
		srv.OnSubscribeMsgChange(func(msg *server.SubscribeMsgChangeEvent) {
			// Do something cool ...

		})

		if err := srv.Serve(w, r); err != nil {
			fmt.Println(r)
			return
		}
	})

	http.ListenAndServe(":8082", nil)
}

DecryptUserInfo 解析用户信息时签名格式错误

// 拼凑签名

func createSignature(parts ...string) string {
	sort.Strings(parts)
	raw := sha1.Sum([]byte(strings.Join(parts, "")))

	return hex.EncodeToString(raw[:])
}

sort.Strings(parts) 导致不符合 sha1( rawData + session_key ) 格式,签名校验失败

是否可以添加自定义 http client 的功能呢?

现在库里的所有 http 请求都是直接调用 http.Get 或者 http.Post 的,如果想要在做这些请求前后做一些操作没法实现,所以希望能添加大致这样的功能:

// HTTPClient 是一个泛化的 http.Client
type HTTPClient interface {
    Do(req *http.Request) (*http.Response, error)
}

然后添加例如

LoginEx(client HTTPClient, appID, secret, code string) (lresp LoginResponse, err error)

Login 则改造成

func Login(appID, secret, code string) (lres LoginResponse, err error) {
    return LoginEx(http.DefaultClient, appID, secret, code)
}

如接受我可以提交 PR,谢谢

二维码图片该怎么保存为文件呢?

res, err := coder.UnlimitedAppCode(wechat.GetToken())
		defer res.Body.Close()

		if err != nil {
			util.CheckError(err, 41007)
		}
		
		qrName := fmt.Sprintf("storage/images/qr/qr_item_%d_%d.jpeg", itemId, memberId)
		f, _ := os.Create(qrName)
		defer f.Close()

		contents, err := ioutil.ReadAll(res.Body)
		io.Copy(f, bytes.NewReader(contents))

按上面这种方式保存,图片无法打开

在ocr中各api添加XXXByReader接口

现有的XXX接口(比如DriverLicense)接收一个filename参数。这规定了服务器必须从本地路径获取图片。而这个图片具体存放的方式是根据服务器及存储设计而变化的,很可能不在本地服务器。考虑到此处图片非常敏感,也很可能没有一个安全的外部URL可以供微信服务器访问到。
建议在api设计上,最大程度与服务器的存储设计以及安全考量进行解藕,提议加入:
XXXByData接口,定义:(以DriverLicense举例)
func DriverLicenseByReader(token string, r io.Reader) (*DrivingLicenseResponse, error)

这个验签逻辑是不是判断错了

func (cli *Client) DecryptUserInfo(sessionKey, rawData, encryptedData, signature, iv string) (*UserInfo, error) {

	if encrypt.NewSigner(false, rawData, sessionKey).CompareWith(signature) {
		return nil, errors.New("failed to validate signature")
	}



// 对比签名
func (sign *Signer) CompareWith(signature string) bool {
	return signature == sign.Sign()
}

为什么两个signature相等却判断为签名错误呢,代码应该写错了吧

https://github.com/medivhzhan/miniapp/blob/2e94375d23200d943a2f9cde2444862f7b114706/decrypt.go#L113

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.