Giter VIP home page Giter VIP logo

wecomchan's Introduction

Wecom酱

通过企业微信向微信推送消息的解决方案。包括:

  1. 配置说明(本页下方)
  2. 推送函数(支持多种语言,见本页下方)
  3. 自行搭建的在线服务源码
    1. PHP版搭建说明
    2. Go版说明
    3. Go适配华为函数工作流版本
    4. 阿里云云函数搭建说明
    5. 腾讯云云函数搭建说明 ⚠️ 2022年5月23日起最低月消费9.9,因此不推荐了
    6. 百度智能云函数搭建说明
    7. Python版华为函数工作流搭建说明

🎈 本项目属于方糖推送生态。该生态包含项目如下:

  • Server酱Turbo:支持企业微信、微信服务号、钉钉、飞书群机器人等多通道的在线服务,无需搭建直接使用,每天有免费额度
  • Wecom酱:通过企业微信推送消息到微信的消息推送函数和在线服务方案,开源免费,可自己搭建。支持多语言。
  • PushDeer:可自行搭建的、无需安装APP的开源推送方案。同时也提供安装APP的降级方案给低版本/没有快应用的系统。支持作为Server酱的通道进行推送,所有支持Server酱的软件和插件都能直接整合PushDeer。

企业微信应用消息配置说明

优点:

  1. 一次配置,持续使用
  2. 配置好以后,只需要微信就能收消息,不再需要安装企业微信客户端

PS:消息接口无需认证即可使用,个人用微信就可以注册

具体操作

第一步,注册企业

用电脑打开企业微信官网,注册一个企业

第二步,创建应用

注册成功后,点「管理企业」进入管理界面,选择「应用管理」 → 「自建」 → 「创建应用」

应用名称填入「Server酱」,应用logo到这里下载,可见范围选择公司名。

创建完成后进入应用详情页,可以得到应用ID( agentid )①,应用Secret( secret )②。

注意:secret推送到手机端时,只能在企业微信客户端中查看。

第三步,添加可信IP

2022年6月20日之后创建的应用,需要额外配置可信IP。

在「应用详情页」的最下方,开发者接口分类中,找到「企业可信IP」,点击「配置」,并填入服务器IP即可。

注意,如果你使用云函数等公用IP的云服务,可能需要在(云函数或其他服务的)设置界面中打开「固定公网IP」来获得一个独立的IP。否则有可能报「第三方服务IP」错误。

第四步,获取企业ID

进入「我的企业」页面,拉到最下边,可以看到企业ID③,复制并填到上方。

推送UID直接填 @all ,推送给公司全员。

第五步,推送消息到微信

进入「我的企业」 → 「微信插件」,拉到下边扫描二维码,关注以后即可收到推送的消息。

PS:如果出现接口请求正常,企业微信接受消息正常,个人微信无法收到消息的情况:

  1. 进入「我的企业」 → 「微信插件」,拉到最下方,勾选 “允许成员在微信插件中接收和回复聊天消息”

  2. 在企业微信客户端 「我」 → 「设置」 → 「新消息通知」中关闭 “仅在企业微信中接受消息” 限制条件

第六步,通过以下函数发送消息:

PS:为使用方便,以下函数没有对 access_token 进行缓存。对于个人低频调用已经够用。带缓存的实现可查看 index.php 中的示例代码(依赖Redis实现)。

PHP版:

function send_to_wecom($text, $wecom_cid, $wecom_aid, $wecom_secret,  $wecom_touid = '@all')
{
    $info = @json_decode(file_get_contents("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=".urlencode($wecom_cid)."&corpsecret=".urlencode($wecom_secret)), true);
                
    if ($info && isset($info['access_token']) && strlen($info['access_token']) > 0) {
        $access_token = $info['access_token'];
        $url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token='.urlencode($access_token);
        $data = new \stdClass();
        $data->touser = $wecom_touid;
        $data->agentid = $wecom_aid;
        $data->msgtype = "text";
        $data->text = ["content"=> $text];
        $data->duplicate_check_interval = 600;

        $data_json = json_encode($data);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json);

        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        
        $response = curl_exec($ch);
        return $response;
    }
    return false;
}

使用实例:

$ret = send_to_wecom("推送测试\r\n测试换行", "企业ID③", "应用ID①", "应用secret②");
print_r( $ret );

PYTHON版:

import json,requests,base64
def send_to_wecom(text,wecom_cid,wecom_aid,wecom_secret,wecom_touid='@all'):
    get_token_url = f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={wecom_cid}&corpsecret={wecom_secret}"
    response = requests.get(get_token_url).content
    access_token = json.loads(response).get('access_token')
    if access_token and len(access_token) > 0:
        send_msg_url = f'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={access_token}'
        data = {
            "touser":wecom_touid,
            "agentid":wecom_aid,
            "msgtype":"text",
            "text":{
                "content":text
            },
            "duplicate_check_interval":600
        }
        response = requests.post(send_msg_url,data=json.dumps(data)).content
        return response
    else:
        return False

def send_to_wecom_image(base64_content,wecom_cid,wecom_aid,wecom_secret,wecom_touid='@all'):
    get_token_url = f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={wecom_cid}&corpsecret={wecom_secret}"
    response = requests.get(get_token_url).content
    access_token = json.loads(response).get('access_token')
    if access_token and len(access_token) > 0:
        upload_url = f'https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token={access_token}&type=image'
        upload_response = requests.post(upload_url, files={
            "picture": base64.b64decode(base64_content)
        }).json()
        if "media_id" in upload_response:
            media_id = upload_response['media_id']
        else:
            return False

        send_msg_url = f'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={access_token}'
        data = {
            "touser":wecom_touid,
            "agentid":wecom_aid,
            "msgtype":"image",
            "image":{
                "media_id": media_id
            },
            "duplicate_check_interval":600
        }
        response = requests.post(send_msg_url,data=json.dumps(data)).content
        return response
    else:
        return False

def send_to_wecom_markdown(text,wecom_cid,wecom_aid,wecom_secret,wecom_touid='@all'):
    get_token_url = f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={wecom_cid}&corpsecret={wecom_secret}"
    response = requests.get(get_token_url).content
    access_token = json.loads(response).get('access_token')
    if access_token and len(access_token) > 0:
        send_msg_url = f'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={access_token}'
        data = {
            "touser":wecom_touid,
            "agentid":wecom_aid,
            "msgtype":"markdown",
            "markdown":{
                "content":text
            },
            "duplicate_check_interval":600
        }
        response = requests.post(send_msg_url,data=json.dumps(data)).content
        return response
    else:
        return False

使用实例:

ret = send_to_wecom("推送测试\r\n测试换行", "企业ID③", "应用ID①", "应用secret②");
print( ret );
ret = send_to_wecom('<a href="https://www.github.com/">文本中支持超链接</a>', "企业ID③", "应用ID①", "应用secret②");
print( ret );
ret = send_to_wecom_image("此处填写图片Base64", "企业ID③", "应用ID①", "应用secret②");
print( ret );
ret = send_to_wecom_markdown("**Markdown 内容**", "企业ID③", "应用ID①", "应用secret②");
print( ret );

TypeScript 版:

import request from 'superagent'

async function sendToWecom(body: {
  text: string
  wecomCId: string
  wecomSecret: string
  wecomAgentId: string
  wecomTouid?: string
}): Promise<{ errcode: number; errmsg: string; invaliduser: string }> {
  body.wecomTouid = body.wecomTouid ?? '@all'
  const getTokenUrl = `https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${body.wecomCId}&corpsecret=${body.wecomSecret}`
  const getTokenRes = await request.get(getTokenUrl)
  const accessToken = getTokenRes.body.access_token
  if (accessToken?.length <= 0) {
    throw new Error('获取 accessToken 失败')
  }
  const sendMsgUrl = `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${accessToken}`
  const sendMsgRes = await request.post(sendMsgUrl).send({
    touser: body.wecomTouid,
    agentid: body.wecomAgentId,
    msgtype: 'text',
    text: {
      content: body.text,
    },
    duplicate_check_interval: 600,
  })
  return sendMsgRes.body
}

使用实例:

sendToWecom({
  text: '推送测试\r\n测试换行',
  wecomAgentId: '应用ID①',
  wecomSecret: '应用secret②',
  wecomCId: '企业ID③',
})
  .then((res) => {
    console.log(res)
  })
  .catch((err) => {
    console.log(err)
  })

.NET Core 版:

using System;
using RestSharp;
using Newtonsoft.Json;
namespace WeCom.Demo
{
    class WeCom
    {   
        public  string SendToWeCom(
            string text,// 推送消息
            string weComCId,// 企业Id①
            string weComSecret,// 应用secret②
            string weComAId,// 应用ID③
            string weComTouId = "@all")
        {
            // 获取Token
            string getTokenUrl = $"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={weComCId}&corpsecret={weComSecret}";
            string token = JsonConvert
            .DeserializeObject<dynamic>(new RestClient(getTokenUrl)
            .Get(new RestRequest()).Content).access_token;
            System.Console.WriteLine(token);
            if (!String.IsNullOrWhiteSpace(token))
            {
                var request = new RestRequest();
                var client = new RestClient($"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={token}");
                var data = new
                {
                    touser = weComTouId,
                    agentid = weComAId,
                    msgtype = "text",
                    text = new
                    {
                        content = text
                    },
                    duplicate_check_interval = 600
                };
                string serJson = JsonConvert.SerializeObject(data);
                System.Console.WriteLine(serJson);
                request.Method = Method.POST;
                request.AddHeader("Accept", "application/json");
                request.Parameters.Clear();
                request.AddParameter("application/json", serJson, ParameterType.RequestBody);
                return client.Execute(request).Content;
            }
            return "-1";
        }
}

使用实例:

   static void Main(string[] args)
        {   // 测试
            Console.Write(new WeCom().SendToWeCom(
            "msginfo",
            "企业Id①"
            , "应用secret②",
            "应用ID③"
            ));
        }

    }

其他版本的函数可参照上边的逻辑自行编写,欢迎PR。

发送图片、卡片、文件或 Markdown 消息的高级用法见 企业微信API

wecomchan's People

Contributors

aceattorney avatar arkylin avatar easychen avatar fcbhank avatar imhuanghua avatar lyc8503 avatar lyj0309 avatar mapxn avatar oct1a avatar psc-f avatar q629988171 avatar riba2534 avatar superhuangxu avatar xsu-git 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

wecomchan's Issues

[求助] 程序报错:无效路径、JSON

复现

阿里云云函数搭建说明 完成部署

发送请求
image
报错:{"code": -6, "msg": "invalid path info"}

将程序中的 if path_info == "/": 改为 if True: 来尝试跳过此问题。

再次发送相同请求
报错:{"code": -1, "msg": "invalid json input"}

日志

FC Invoke Start RequestId: 1-64dd3eef-ba945220b77ec629e29c7ed9

2023-08-17 05:26:07 1-64dd3eef-ba945220b77ec629e29c7ed9 [INFO] request body: 
2023-08-17 05:26:07 1-64dd3eef-ba945220b77ec629e29c7ed9 [INFO] path_info: None
None
FC Invoke End RequestId: 1-64dd3eef-ba945220b77ec629e29c7ed9

PHP版搭建说明 完成部署

发送请求

# python

import requests
import json

url = "http://x.xxx:5039/"
headers = {
    "Content-Type": "application/json"
}

data = {
    "sendkey": "x",
    "msg_type": "markdown",
    "msg": "Markdown Here!"
}

response = requests.post(url, headers=headers, data=json.dumps(data))

if response.status_code == 200:
    print("POST request sent successfully!")
    print("Response:")
    print(response.text)
else:
    print("Failed to send POST request. Status code:", response.status_code)

回复:bad params

我的操作有问题吗,为什么会这样?

docker版怎么用POST请求发送文本消息

# curl -X POST -d '{"sendkey":"mysendkey","msg_type":"text","msg":"test msg"}' 'http://localhost:48080/wecomchan'
curl: (52) Empty reply from server
查看wecomchan的日志
2022/05/10 01:23:33 wecomchan.go:94: 企业微信获取access_token接口返回==> map[access_token:a_valid_token errcode:0 errmsg:ok expires_in:7200]
2022/05/10 01:23:33 wecomchan.go:248: sendkey 错误,请检查
2022/05/10 01:23:33 server.go:3139: http: panic serving 172.17.0.1:40915: sendkey 错误,请检查

请问大佬是docker版的不支持POST发送文本消息吗?
不支持的话能不能增加这个功能呢?现实情况下,消息可能会有很多行,用GET发送实在太不方便了。
万分感谢

PHP 版能否使用 post 参数

能不能用 post 方式使用 wecom 酱的 php 版本

post body 为{"msg": "post_test","test": "text1"},php 设置为echo send_to_wecom(@$_POST['msg'], WECOM_CID, WECOM_AID, WECOM_SECRET, WECOM_TOUID);

尝试过下列修改:

  • php 配置 enable_post_data_reading=on
  • $raw = file_get_contents('php://input'); echo $raw;
  • hearder 修改 Content-Type=application/x-www-data-urlencoded

但是,均提示 empty content,无法从 $_POST['msg'] 获取数值。

Post发送图片失败

使用腾讯云搭建
url:
https://service-jr67p19f-xx.cd.apigw.tencentcs.com/release/wecomchan

body:
{
"msg": "data:image/png;base64,xxxx",
"sendkey": "Time",
"msg_type": "image"
}

返回体:
{
"code": 0,
"msg": "invalid media_id, hint: [1650280848252373435330496], from ip: 119.27.187.239, more info at https://open.work.weixin.qq.com/devtool/query?e=40007"
}

消息换行

大佬们:

  • README.md中的PHP版本发送"推送测试\r\n测试换行"可以正常换行;
  • 服务器部署PHP版本后,发送消息不支持换行(https://x.xxx.xxx/?sendkey=sendkey&text=推送测试\r\n测试换行
    有人遇到这个问题吗?求助

.NET Core实例

请问C#实例中 RestSharp 使用的是什么版本。我使用RestSharp107.3.0有以下报错,框架是.NET Core 3.0
wecomchan1
wecomchan2
wecomchan3
wecomchan4

关于多个应用的推送的疑问?

想请教一下,如果企业微信里面新建了两个不同的应用,可以用一个docker分别实现两个应用的推送么?就是根据应用ID选择性的推送?

云函数报错

{"errorCode":1,"errorMessage":"Resource limit exceed for function [dingdong]","requestId":"1a04a1006a8d321f05031308b4fd695d","statusCode":432}

17点前正常使用,17点后突然Python使用报错

报错代码

  File "D:\test\微信发送消息\birthday_12.py", line 6, in <module>
    from wechat import send_to_wecom
  File "D:\test\微信发送消息\wechat.py", line 30, in <module>
    ret = send_to_wecom("测试接收", "ww9e97b57d9a9c31f7", "1000004", "D8fCnOs2zat-jup5a2bws9riDn3PS-4AM3HHfsjOb7U")
  File "D:\test\微信发送消息\wechat.py", line 11, in send_to_wecom
    response = requests.get(get_token_url).content
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\api.py", line 75, in get
    return request('get', url, params=params, **kwargs)
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\api.py", line 61, in request
    return session.request(method=method, url=url, **kwargs)
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\sessions.py", line 542, in request
    resp = self.send(prep, **send_kwargs)
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\sessions.py", line 655, in send
    r = adapter.send(request, **kwargs)
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\adapters.py", line 439, in send
    resp = conn.urlopen(
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\connectionpool.py", line 696, in urlopen
    self._prepare_proxy(conn)
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\connectionpool.py", line 964, in _prepare_proxy
    conn.connect()
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\connection.py", line 359, in connect
    conn = self._connect_tls_proxy(hostname, conn)
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\connection.py", line 500, in _connect_tls_proxy
    return ssl_wrap_socket(
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\util\ssl_.py", line 453, in ssl_wrap_socket
    ssl_sock = _ssl_wrap_socket_impl(sock, context, tls_in_tls)
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\util\ssl_.py", line 495, in _ssl_wrap_socket_impl
    return ssl_context.wrap_socket(sock)
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\ssl.py", line 500, in wrap_socket
    return self.sslsocket_class._create(
  File "C:\Users\chir\AppData\Local\Programs\Python\Python39\lib\ssl.py", line 997, in _create
    raise ValueError("check_hostname requires server_hostname")
ValueError: check_hostname requires server_hostname

进程已结束,退出代码为 1

Python版本:3.9

docker版本的容器时间错误,默认GMT+0且没法修改

功能正常,但看了下日志,发送时间跟实际时间差了8小时,估计是用了GMT+0的时区。

但创建容器时使用环境变量TZ=Asia/Shanghai发现没有用

然后准备进容器里面修改,发现容器没有内置bin/sh文件,进不去命令行

不知道大家用docker版本有没有这个问题,怎么解决呢?

个人微信无法收到消息

原版逻辑复写成python
实际测试下来 企微可以收到接口消息 微信不能
不知道哪里出现错误 求解

腾讯报错

{"errorCode":1,"errorMessage":"Process exited before completing request","requestId":"954a3c773d9e987c100b46927b4d01d5","statusCode":439}

阿里云函数

使用阿里云函数实现功能时发现,在配置可信ip时需要先配置可信域名,而无法继续

golang context sdk 使用问题

golang sdk 中的 context 应该使用 goroutine 的 context, 如果使用全局的 context 失去了作用.
如果作者忙, 我可以提交一个 pr

之前好像需要备案,现在又可以了

需要先搭一个服务通过企业微信的接收消息服务器认证(相关代码不知道不能能集成下),然后就可以不用备案设置可信IP,然后搭wecomchan就可以
image

无需云函数的 Python 库

之前受到 wecomchan 项目启发, 编写了一个可以直接在本地调用的 Python 库. (就是在本地直接调用企业微信的 API)
https://github.com/lyc8503/WechatPush
可以直接 pip3 install wechat_push 安装使用.

在某些场景下使用比部署在云函数上再去调用更加方便, 也不需要服务器.

不知道作者是否介意在 README 中添加我这个项目的链接, 让大家根据情况自行选择 :)

请问怎么发送给指定的人

用的是docker,启动容器时参数WECOM_TOUID="@ALL",那在命令行运行时怎么指定发给某人或者某几个人?
需要每次重新建立容器然后TOUID=@XX? 还是在调用的命令行后面直接加参数?这样应该比较合理。
再就是这个企业微信的TOUID怎么获取,文档里链接的企业微信官方文档打开是空白的。

关于我在2022/09/08日的搭建过程中,发现的配置问题

在白名单那里走了很多的弯路,原以为可以很快的搭建好的,毕竟我比较小白,过程就不说了。
说说解决方法,按照平常的方法搭建,最后测试Hello的时候会显示是不信任的IP,解决方法如下
打开云函数控制台,进入wecomchan项目的函数配置,然后找到下面的公网IP,进行勾选,等待一段时候后,
会分配一个固定的公网IP!
将此IP添加进 企业微信的可信IP 这样方可以按照教程在调用云函数,实现之前的功能

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.