├── .gitignore ├── .gitmodules ├── Dockerfile ├── LICENSE ├── README.md ├── build.bat ├── build.sh ├── config └── config.go ├── go.mod ├── go.sum ├── main.go ├── pic ├── Snow.jpg └── 截图.png ├── server ├── server.go └── type.go └── transmit ├── msgforward.pb.go └── picdump.pb.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | # ./pic downloaded picture. 18 | pic/*.jpg 19 | pic/*.png 20 | 21 | # Goland directories 22 | .idea -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "setuDumpProto"] 2 | path = setuDumpProto 3 | url = https://github.com/zhangyu0310/setuDumpProto.git 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:7 2 | MAINTAINER poppinzhang zhangyu960310@gmail.com 3 | WORKDIR /root 4 | USER root 5 | RUN set -eux; \ 6 | yum install -y wget tar bzip2 gzip; \ 7 | wget https://github.com/zhangyu0310/wechat-setu-bot/releases/download/v0.2.0/setu_server.tgz; \ 8 | tar xzf setu_server.tgz; 9 | 10 | CMD while true;do cd /root/wechat-setu-bot;./start_up.sh;sleep 30; done 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 YuZhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 企业微信机器人-随机色图 2 | 3 | 通过企业微信机器人,定时向企业微信群中推送随机色图。一次会推送三种格式,`News` `Text` `Image` (因为企业微信有屏蔽功能,可能导致一些`News`格式推送的图片没办法打开,所以加上了后面两种。同时,`Text` 可以设置是否@所有人) 4 | 5 |
6 | avatar 7 |
8 | 9 | ## 快速开始 10 | 11 | 使用docker快速搭建基础功能的色图服务。 12 | 13 | ```shell 14 | # 省略git和docker的安装过程,建议使用apt或yum快速解决 15 | git clone https://github.com/zhangyu0310/wechat-setu-bot.git 16 | cd wechat-setu-bot 17 | docker build -t centos:setubot . 18 | # 在 WECHAT_WEBHOOK="" 的双引号里,填入企业微信机器人的webhook 19 | docker run --name setu -e WECHAT_WEBHOOK="" -d centos:setubot 20 | ``` 21 | 22 | 或者 23 | 24 | ``` 25 | # 无需要git,不拉源码,直接下载docker镜像 26 | docker pull zhangyu0310/centos:setubot 27 | # 在 WECHAT_WEBHOOK="" 的双引号里,填入企业微信机器人的webhook 28 | docker run --name setu -e WECHAT_WEBHOOK="" -d centos:setubot 29 | ``` 30 | 31 | > 第一种方法相对节省流量,docker build是在本地完成的。第二种方法看起来简单一些,不过需要拉镜像,会消耗一定流量。
32 | > 使用docker部署,仅提供了最基础的功能,没有图片转储、消息转发之类的能力。复杂功能需要参照下面的参数说明配置。 33 | 34 | ## 参数说明 35 | 36 | ```shell 37 | .\setuServer.exe -at-all -dl-dir="./pic" -pic-msg -wechat-url="xxx" -intervals=3600 -r18 38 | ``` 39 | 40 | | 参数 | 说明 | 备注 | 41 | |:------------------:|:--------------:|:------------------------------------:| 42 | | `-at-all` | 具有该选项会自带@所有人 | bool类型 | 43 | | `-dl-dir` | 指定下载图片的路径 | 仅在`-pic-msg`被指定时生效 | 44 | | `-dump-server` | 图片转储服务器信息 | ip:port | 45 | | `-dump-url` | 图片转储域名 | 推送`Text`格式消息的转储图片Url | 46 | | `-help` | 帮助 | | 47 | | `-intervals` | 色图推送间隔时间(单位:秒) | 默认60,最小10(别把人家色图服务搞挂了!) | 48 | | `-keep` | 保留本地原图 | 推送`Image`消息需下载图片,该参数表示是否保留图片。默认为true | 49 | | `-news-msg` | 是否推送`News`消息 | bool类型,默认为 true | 50 | | `-once` | 执行一次推送后立即退出 | 将定时逻辑分离,可使用 crontab 等外部定时机制实现更灵活的控制 | 51 | | `-pic-dump` | 是否开启图片转储 | bool类型 | 52 | | `-pic-msg` | 是否推送`Image`消息 | bool类型,有这个参数的具体原因见下 | 53 | | `-pic-size` | 可以下载不同尺寸的图片 | 默认为`original` | 54 | | `-r18` | 懂得都懂 | bool类型 | 55 | | `-setu-api-url` | 色图API Url | 默认为`https://api.lolicon.app/setu/v1` | 56 | | `-setu-transmit` | 消息传递 | 将所有消息传递至转储服务,由其广播分发,具体见下 | 57 | | `-tags` | 图片标签 | 可以指定推送某种带有某个标签的图片 | 58 | | `-transmit-server` | 消息传递目标服务器 | ip:port | 59 | | `-version` | 打印版本 | | 60 | | `-wechat-url` | 微信机器人Webhook地址 | 必填 | 61 | 62 | ## 使用方法 63 | 64 | 1. 创建一个新的企业微信机器人,获取到它的Webhook地址 65 | 2. 将地址填入参数`-wechat-url` 启动服务 66 | 67 | ## 关于`-pic-msg`这个参数 68 | 69 | 企业微信会屏蔽一些网站的`News`消息,为了能在点进链接前看到是否是喜欢的图片,这里做了`Image`消息的推送。 70 | 71 | `Image`消息需要先将图片下载到本地(所以需要指定`-dl-dir`图片的下载路径) 72 | 73 | 由于企业微信要求图片大小不能超过2M,所以对下载好的图片进行了大小调整。群里看到的很有可能不是原图。 74 | 75 | ## 关于图片转储功能 76 | 77 | 由于pixiv及其代理服务经常被墙,输出的域名经常无法打开。所以提供了转储功能,可以将服务部署在墙外服务器上,然后转储到墙内服务器。这个功能需要墙内的转储接收模块配合使用。 78 | 79 | [EasyPicServer](https://github.com/zhangyu0310/EasyPicServer) 80 | 81 | ## 关于消息传递 82 | 83 | 如果不希望只推送到一个webhook上,可以将消息转发至[EasyPicServer](https://github.com/zhangyu0310/EasyPicServer),由其代为广播。它已经实现了简单的webhook注册页面,开箱即用。为什么不直接使用这个服务广播呢?为了节省跨境服务器的流量费用。。。 84 | 85 | ## 效果截图 86 | 87 |
88 | avatar 89 |
90 | 91 | ## 感谢 92 | 93 | THX [Lolicon API](https://api.lolicon.app/) 94 | 95 | 愿我们的未来能有光芒照耀! 96 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | protoc -I ./setuDumpProto picdump.proto --go_out=plugins=grpc:./transmit 2 | protoc -I ./setuDumpProto msgforward.proto --go_out=plugins=grpc:./transmit 3 | go build -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | protoc -I ./setuDumpProto picdump.proto --go_out=plugins=grpc:./transmit 4 | protoc -I ./setuDumpProto msgforward.proto --go_out=plugins=grpc:./transmit 5 | go build -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "sync/atomic" 5 | ) 6 | 7 | // Config Configurations of server. 8 | type Config struct { 9 | SetuApiUrl string 10 | WeChatUrl string 11 | Intervals uint 12 | R18 bool 13 | AtAll bool 14 | PicMsg bool 15 | NewsMsg bool 16 | PicDownloadDir string 17 | PicDump bool 18 | DumpServer string 19 | DumpUrl string 20 | SetuTransmit bool 21 | TransmitServer string 22 | Tags []string 23 | PicSize []string 24 | Once bool 25 | Keep bool 26 | } 27 | 28 | var ( 29 | globalConf atomic.Value 30 | ) 31 | 32 | // InitializeConfig initialize the global config handler. 33 | func InitializeConfig(enforceCmdArgs func(*Config)) { 34 | cfg := Config{} 35 | // Use command config cover config file. 36 | enforceCmdArgs(&cfg) 37 | StoreGlobalConfig(&cfg) 38 | } 39 | 40 | // GetGlobalConfig returns the global configuration for this server. 41 | // It should store configuration from command line and configuration file. 42 | // Other parts of the system can read the global configuration use this function. 43 | func GetGlobalConfig() *Config { 44 | return globalConf.Load().(*Config) 45 | } 46 | 47 | // StoreGlobalConfig stores a new config to the globalConf. It mostly uses in the test to avoid some data races. 48 | func StoreGlobalConfig(config *Config) { 49 | globalConf.Store(config) 50 | } 51 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module setuServer 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/golang/protobuf v1.5.2 // indirect 7 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 8 | google.golang.org/grpc v1.42.0 9 | google.golang.org/protobuf v1.27.1 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 5 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 6 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 7 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 8 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 9 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 10 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 11 | github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 12 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 13 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 14 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 15 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 16 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 17 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 18 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 19 | github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 20 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 21 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 22 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 23 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 24 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 25 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 26 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 27 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 28 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 29 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 30 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 31 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 32 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 33 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 34 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 35 | github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= 36 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 37 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 38 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 39 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 40 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 41 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 42 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 43 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 44 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 45 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 46 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 47 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 48 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= 49 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= 50 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 51 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 52 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 53 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 54 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 55 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 56 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 57 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 58 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 59 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 60 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 61 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 62 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 63 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 64 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 65 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 66 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 67 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 68 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 69 | golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= 70 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 71 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 72 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 73 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 74 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 75 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 76 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 77 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 78 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 79 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 80 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= 81 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 82 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 83 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 84 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 85 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 86 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 87 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 88 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 89 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 90 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 91 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 92 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 93 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 94 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 95 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 96 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= 97 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 98 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 99 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 100 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 101 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 102 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 103 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 104 | google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= 105 | google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= 106 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 107 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 108 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 109 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 110 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 111 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 112 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 113 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 114 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 115 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 116 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 117 | google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= 118 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 119 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 120 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 121 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 122 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 123 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 124 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 125 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | "setuServer/config" 8 | "setuServer/server" 9 | "strings" 10 | ) 11 | 12 | var ( 13 | setuApiUrl = flag.String("setu-api-url", "https://api.lolicon.app/setu/v2", "Api Url of setu") 14 | wechatUrl = flag.String("wechat-url", "", "Wechat Web Hook Url") 15 | intervals = flag.Uint("intervals", 60, "Intervals of post setu.(second) [Minimum is 10s]") 16 | r18 = flag.Bool("r18", false, "Post R18 picture") 17 | atAll = flag.Bool("at-all", false, "@all group member") 18 | picMsg = flag.Bool("pic-msg", false, "Download picture & send picture msg") 19 | newsMsg = flag.Bool("news-msg", true, "Send picture use news message") 20 | dlDir = flag.String("dl-dir", "./", "Dir of download picture") 21 | picDump = flag.Bool("pic-dump", false, "Dump setu pictures to local server") 22 | dumpServer = flag.String("dump-server", "", "Server info to dump pictures") 23 | dumpUrl = flag.String("dump-url", "", "Url for user get pictures") 24 | setuTransmit = flag.Bool("setu-transmit", false, "Transmit setu messages to local server") 25 | transmitServer = flag.String("transmit-server", "", "Server info to transmit setu") 26 | tags = flag.String("tags", "", "Tags of pictures") 27 | picSize = flag.String("pic-size", "original", "Size list of pictures") 28 | once = flag.Bool("once", false, "Run once and exit") 29 | keep = flag.Bool("keep", true, "Keep local original image") 30 | ) 31 | 32 | // tagsContentAnalysis analyze tags according to rules 33 | func tagsContentAnalysis(tagStr string) (tagArr []string) { 34 | tagArr = append(tagArr, tagStr) 35 | return 36 | } 37 | 38 | // getPicSize get pictures size list 39 | func getPicSize(sizeStr string) (sizeArr []string) { 40 | // Note: 'original' size must be exists!!! 41 | sizeArr = strings.Split(sizeStr, "|") 42 | for _, v := range sizeArr { 43 | if v == "original" { 44 | return 45 | } 46 | } 47 | sizeArr = append(sizeArr, "original") 48 | return 49 | } 50 | 51 | // cmdConfigSetToGlobal store command config to global config. 52 | func cmdConfigSetToGlobal(cfg *config.Config) { 53 | cfg.SetuApiUrl = *setuApiUrl 54 | cfg.WeChatUrl = *wechatUrl 55 | cfg.Intervals = *intervals 56 | cfg.R18 = *r18 57 | cfg.AtAll = *atAll 58 | cfg.PicMsg = *picMsg 59 | cfg.NewsMsg = *newsMsg 60 | cfg.PicDownloadDir = *dlDir 61 | cfg.PicDump = *picDump 62 | cfg.DumpServer = *dumpServer 63 | cfg.DumpUrl = *dumpUrl 64 | cfg.SetuTransmit = *setuTransmit 65 | cfg.TransmitServer = *transmitServer 66 | cfg.Tags = tagsContentAnalysis(*tags) 67 | cfg.PicSize = getPicSize(*picSize) 68 | cfg.Once = *once 69 | cfg.Keep = *keep 70 | } 71 | 72 | func main() { 73 | help := flag.Bool("help", false, "show the usage") 74 | version := flag.Bool("version", false, "version of server") 75 | flag.Parse() 76 | if *help { 77 | flag.Usage() 78 | os.Exit(0) 79 | } 80 | if *version { 81 | // TODO: Print version info.(Version/GitCommit/CompileTime...) 82 | log.Println("Version: v0.2.0") 83 | os.Exit(0) 84 | } 85 | if *wechatUrl == "" && *transmitServer == "" { 86 | log.Println("Error! Both of Wechat hook url and transmit server are empty.") 87 | flag.Usage() 88 | os.Exit(1) 89 | } 90 | config.InitializeConfig(cmdConfigSetToGlobal) 91 | server.Run() 92 | } 93 | -------------------------------------------------------------------------------- /pic/Snow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poppinzhang/wechat-setu-bot/710af9badb0f635384564e202ea44088c4c0630e/pic/Snow.jpg -------------------------------------------------------------------------------- /pic/截图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poppinzhang/wechat-setu-bot/710af9badb0f635384564e202ea44088c4c0630e/pic/截图.png -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "crypto/md5" 7 | "encoding/base64" 8 | "encoding/hex" 9 | "encoding/json" 10 | "errors" 11 | "fmt" 12 | "image" 13 | _ "image/jpeg" 14 | "image/png" 15 | "io" 16 | "io/ioutil" 17 | "log" 18 | "net/http" 19 | "os" 20 | "setuServer/config" 21 | "setuServer/transmit" 22 | "strings" 23 | "time" 24 | 25 | "github.com/nfnt/resize" 26 | "google.golang.org/grpc" 27 | ) 28 | 29 | func dumpPictureToLocalServer(result *Result, dumpClient transmit.PicCourierClient, dumpUrl string) { 30 | for index, setu := range result.Setus { 31 | name, err := getPictureName(setu.Urls.Original) 32 | if err != nil { 33 | log.Println(err) 34 | continue 35 | } 36 | picFile, err := os.OpenFile(result.picPaths[index], os.O_RDONLY, 0666) 37 | if err != nil { 38 | continue 39 | } 40 | pic, err := ioutil.ReadAll(picFile) 41 | if err != nil { 42 | log.Println(err) 43 | _ = picFile.Close() 44 | continue 45 | } 46 | reply, err := dumpClient.SendPic(context.Background(), &transmit.PicRequest{Pic: pic, PicName: name}) 47 | if err != nil { 48 | log.Println("Call dump pictures rpc failed.", err) 49 | } else { 50 | log.Println("Dump pictures success!", reply.Message) 51 | result.Setus[index].DumpUrl = dumpUrl + name 52 | } 53 | _ = picFile.Close() 54 | } 55 | } 56 | 57 | func transmitSetu(courier transmit.SetuCourierClient, messages []BotMsgReq) { 58 | for _, msg := range messages { 59 | article := msg.News.Articles[0] 60 | setuReq := transmit.SeTuRequest{Title: article.Title, 61 | Desc: article.Description, 62 | OriginalUrl: article.Picurl, 63 | Url: msg.Text.Content, 64 | PicBase64: msg.Image.Base64, 65 | PicMd5: msg.Image.Md5} 66 | reply, err := courier.SendSuTu(context.Background(), &setuReq) 67 | if err != nil { 68 | log.Println("Call dump pictures rpc failed.", err) 69 | } else { 70 | log.Println("Dump pictures success!", reply.ErrMessage) 71 | } 72 | } 73 | } 74 | 75 | func postSetuNews(result Result, transmitMsg *[]BotMsgReq) (err error) { 76 | for i := 0; i < len(result.Setus); i++ { 77 | setu := result.Setus[i] 78 | desc := fmt.Sprintf("Author: %s, Tags: ", setu.Author) 79 | for _, tag := range setu.Tags { 80 | desc += tag + " | " 81 | } 82 | article := Article{Title: setu.Title, 83 | Description: desc, 84 | Url: setu.Urls.Original, 85 | Picurl: setu.Urls.Original} 86 | var articles []Article 87 | articles = append(articles, article) 88 | news := &News{Articles: articles} 89 | postNews := BotMsgReq{MsgType: BotMsgNews, News: news} 90 | err = postSetuToWeChat(postNews) 91 | if err != nil { 92 | log.Println("Post setu news failed.") 93 | return 94 | } 95 | (*transmitMsg)[i].News = news 96 | } 97 | return 98 | } 99 | 100 | func postSetuText(result Result, atAll bool, transmitMsg *[]BotMsgReq) { 101 | for i := 0; i < len(result.Setus); i++ { 102 | setu := result.Setus[i] 103 | var MentionedList []string 104 | if atAll { 105 | MentionedList = append(MentionedList, "@all") 106 | } 107 | content := setu.Urls.Original 108 | if setu.DumpUrl != "" { 109 | content = setu.DumpUrl 110 | } 111 | txt := &Text{ 112 | Content: content, 113 | MentionedList: MentionedList, 114 | } 115 | postText := BotMsgReq{ 116 | MsgType: BotMsgText, 117 | Text: txt, 118 | } 119 | err := postSetuToWeChat(postText) 120 | if err != nil { 121 | log.Println("Post setu text failed.") 122 | } 123 | (*transmitMsg)[i].Text = txt 124 | } 125 | } 126 | 127 | func postSetuPic(result Result, transmitMsg *[]BotMsgReq) { 128 | for i := 0; i < len(result.Setus); i++ { 129 | picPath := result.getPicPath(uint(i)) 130 | compress := false 131 | fileInfo, err := os.Stat(picPath) 132 | if err != nil { 133 | log.Println(err) 134 | break 135 | } 136 | picDataSize := fileInfo.Size() 137 | picFile, err := os.OpenFile(picPath, os.O_RDONLY, 0666) 138 | if err != nil { 139 | return 140 | } 141 | picData, err := ioutil.ReadAll(picFile) 142 | if err != nil { 143 | return 144 | } 145 | err = picFile.Close() 146 | if err != nil { 147 | return 148 | } 149 | for round := 0; round < 5; round++ { 150 | if picDataSize > 2*1024*1024 { 151 | picData, err = picCompress(picData) 152 | if err != nil { 153 | log.Println(err) 154 | break 155 | } 156 | picDataSize = int64(len(picData)) 157 | } else { 158 | compress = true 159 | break 160 | } 161 | } 162 | if compress { 163 | picBase64 := base64.StdEncoding.EncodeToString(picData) 164 | md5Hash := md5.New() 165 | md5Hash.Write(picData) 166 | md5Str := hex.EncodeToString(md5Hash.Sum(nil)) 167 | 168 | img := &Image{Base64: picBase64, Md5: md5Str} 169 | postPic := BotMsgReq{MsgType: BotMsgImage, Image: img} 170 | err = postSetuToWeChat(postPic) 171 | if err != nil { 172 | log.Println(err) 173 | } 174 | _ = picFile.Close() 175 | (*transmitMsg)[i].Image = img 176 | } 177 | } 178 | } 179 | 180 | // Run The main loop to send setu on time. 181 | func Run() { 182 | cfg := config.GetGlobalConfig() 183 | var dumpClient transmit.PicCourierClient 184 | var setuClient transmit.SetuCourierClient 185 | 186 | intervals := cfg.Intervals 187 | if intervals < 10 { 188 | intervals = 10 189 | } 190 | 191 | if cfg.PicDump { 192 | picConn, err := grpc.Dial(cfg.DumpServer, grpc.WithInsecure(), 193 | grpc.WithDefaultCallOptions( 194 | grpc.MaxCallSendMsgSize(31457280), 195 | grpc.MaxCallRecvMsgSize(31457280))) 196 | if err != nil { 197 | log.Println("Connect dump server failed.", err) 198 | os.Exit(-1) 199 | } 200 | setuConn, err := grpc.Dial(cfg.TransmitServer, grpc.WithInsecure(), 201 | grpc.WithDefaultCallOptions( 202 | grpc.MaxCallSendMsgSize(31457280), 203 | grpc.MaxCallRecvMsgSize(31457280))) 204 | if err != nil { 205 | log.Println("Connect transmit server failed.", err) 206 | os.Exit(-1) 207 | } 208 | dumpClient = transmit.NewPicCourierClient(picConn) 209 | setuClient = transmit.NewSetuCourierClient(setuConn) 210 | } 211 | for { 212 | tick(cfg, dumpClient, setuClient) 213 | if cfg.Once { 214 | break 215 | } 216 | time.Sleep(time.Duration(intervals) * time.Second) 217 | } 218 | } 219 | 220 | func tick(cfg *config.Config, dumpClient transmit.PicCourierClient, setuClient transmit.SetuCourierClient) { 221 | // Get setu info & download setu picture 222 | result, err := getSetuFromApi() 223 | if err != nil { 224 | fmt.Println("Get setu failed.") 225 | return 226 | } 227 | defer func() { 228 | if cfg.Keep { 229 | return 230 | } 231 | if err := result.removeLocalPics(); err != nil { 232 | log.Println(err) 233 | } 234 | }() 235 | if cfg.PicDump { 236 | dumpPictureToLocalServer(&result, dumpClient, cfg.DumpUrl) 237 | } 238 | messages := make([]BotMsgReq, 1) 239 | postSetuText(result, cfg.AtAll, &messages) 240 | // Post setu by different way 241 | if cfg.NewsMsg { 242 | if err := postSetuNews(result, &messages); err != nil { 243 | log.Println(err) 244 | return 245 | } 246 | } 247 | // Post setu pic 248 | if cfg.PicMsg { 249 | postSetuPic(result, &messages) 250 | } 251 | if cfg.SetuTransmit { 252 | transmitSetu(setuClient, messages) 253 | } 254 | } 255 | 256 | func getPictureName(url string) (string, error) { 257 | index := strings.LastIndex(url, "/img/") 258 | if index == -1 { 259 | return "", errors.New("can't find index in url") 260 | } 261 | name := url[index+1:] 262 | name = strings.ReplaceAll(name, "/", "-") 263 | return name, nil 264 | } 265 | 266 | // getSetuFromApi get setu info & download setu 267 | func getSetuFromApi() (result Result, err error) { 268 | cfg := config.GetGlobalConfig() 269 | // TODO: Set Key Word & Num (WeChat not support interact) 270 | // Get setu from setu api 271 | r18 := 0 272 | if cfg.R18 { 273 | r18 = 2 274 | } 275 | 276 | query := Query{R18: r18, Num: 1, Tag: cfg.Tags, Size: cfg.PicSize} 277 | jsonStr, err := json.Marshal(query) 278 | if err != nil { 279 | log.Println("Marshal json failed.", err) 280 | return 281 | } 282 | req, err := http.NewRequest("POST", cfg.SetuApiUrl, bytes.NewBuffer(jsonStr)) 283 | if err != nil { 284 | log.Println("Http request failed.", err) 285 | return 286 | } 287 | req.Header.Add("Content-Type", "application/json") 288 | resp, err := http.DefaultClient.Do(req) 289 | if err != nil { 290 | log.Println("Http Do failed.", err) 291 | return 292 | } 293 | defer func(Body io.ReadCloser) { 294 | _ = Body.Close() 295 | }(resp.Body) 296 | if resp.StatusCode != 200 { 297 | log.Println("Http Get status is", resp.StatusCode, "not 200") 298 | return 299 | } 300 | bodyStr, err := ioutil.ReadAll(resp.Body) 301 | if err != nil { 302 | log.Println("Read Http Get body failed.", err) 303 | return 304 | } 305 | err = json.Unmarshal(bodyStr, &result) 306 | if err != nil { 307 | log.Println("Json unmarshal failed.", err) 308 | return 309 | } 310 | if result.Error != "" { 311 | log.Println("Result error! Error message:", result.Error) 312 | return 313 | } 314 | // Don't need to get picture message, return 315 | if !cfg.PicMsg { 316 | return 317 | } 318 | // Download setu from pic url. 319 | for i := 0; i < len(result.Setus); i++ { 320 | setu := result.Setus[i] 321 | var dlReq *http.Request 322 | var dlResp *http.Response 323 | dlReq, err = http.NewRequest("GET", setu.Urls.Original, bytes.NewBuffer([]byte(""))) 324 | if err != nil { 325 | log.Println(err) 326 | return 327 | } 328 | dlReq.Header.Set("Referer", "https://i.pixiv.cat/") 329 | dlResp, err = http.DefaultClient.Do(dlReq) 330 | if err != nil { 331 | log.Println("Download picture failed.", err) 332 | return 333 | } 334 | var name string 335 | name, err = getPictureName(setu.Urls.Original) 336 | if err != nil { 337 | log.Println(err) 338 | _ = dlResp.Body.Close() 339 | continue 340 | } 341 | path := cfg.PicDownloadDir + "/" + name 342 | var imgFile *os.File 343 | imgFile, err = os.Create(path) 344 | if err != nil { 345 | return 346 | } 347 | _, err = io.Copy(imgFile, dlResp.Body) 348 | if err != nil { 349 | return 350 | } 351 | result.setPicPath(path) 352 | _ = imgFile.Close() 353 | _ = dlResp.Body.Close() 354 | } 355 | return 356 | } 357 | 358 | // postSetuToWeChat post setu to WeChat 359 | func postSetuToWeChat(post BotMsgReq) (err error) { 360 | cfg := config.GetGlobalConfig() 361 | if cfg.WeChatUrl == "" { 362 | return nil 363 | } 364 | postStr, err := json.Marshal(post) 365 | if err != nil { 366 | log.Println("Json marshal post failed.", err) 367 | return 368 | } 369 | respPost, err := http.Post(cfg.WeChatUrl, "application/json", bytes.NewBuffer(postStr)) 370 | if err != nil { 371 | log.Println("Post to wechat failed", err) 372 | return 373 | } 374 | msg, err := ioutil.ReadAll(respPost.Body) 375 | if err != nil { 376 | log.Println(err) 377 | } 378 | log.Println(string(msg)) 379 | _ = respPost.Body.Close() 380 | return 381 | } 382 | 383 | // picCompress Modify size to compress pictures. 384 | func picCompress(picData []byte) (newPicData []byte, err error) { 385 | oldBuf := bytes.NewBuffer(picData) 386 | pic, _, err := image.Decode(oldBuf) 387 | if err != nil { 388 | return 389 | } 390 | newPic := resize.Resize(uint(pic.Bounds().Dx()/2), 0, pic, resize.Lanczos3) 391 | var newBuf bytes.Buffer 392 | err = png.Encode(&newBuf, newPic) 393 | if err != nil { 394 | return 395 | } 396 | newPicData, err = ioutil.ReadAll(&newBuf) 397 | if err != nil { 398 | return 399 | } 400 | return 401 | } 402 | -------------------------------------------------------------------------------- /server/type.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "os" 4 | 5 | // Query get setu info. TODO: Annotate field can be assigned 6 | type Query struct { 7 | R18 int `json:"r18"` 8 | Num int `json:"num"` 9 | // Uid int `json:"uid"` 10 | // KeyWord string `json:"keyword"` 11 | Tag []string `json:"tag"` 12 | Size []string `json:"size"` 13 | // Proxy string `json:"proxy"` 14 | // DateAfter int `json:"dateAfter"` 15 | // DateBefore int `json:"dateBefore"` 16 | // Dsc bool `json:"dsc"` 17 | } 18 | 19 | type Result struct { 20 | Error string `json:"error"` 21 | Setus []Setu `json:"data"` 22 | picPaths []string 23 | } 24 | 25 | func (result *Result) setPicPath(path string) { 26 | result.picPaths = append(result.picPaths, path) 27 | } 28 | 29 | func (result *Result) getPicPath(index uint) string { 30 | return result.picPaths[index] 31 | } 32 | 33 | func (result *Result) removeLocalPics() (err error) { 34 | for _, p := range result.picPaths { 35 | err = os.RemoveAll(p) 36 | if err != nil { 37 | return 38 | } 39 | } 40 | return 41 | } 42 | 43 | type PicUrl struct { 44 | Original string `json:"original"` 45 | Regular string `json:"regular"` 46 | Small string `json:"small"` 47 | Thumb string `json:"thumb"` 48 | Mini string `json:"mini"` 49 | } 50 | 51 | type Setu struct { 52 | Pid int `json:"pid"` 53 | P int `json:"p"` 54 | Uid int `json:"uid"` 55 | Title string `json:"title"` 56 | Author string `json:"author"` 57 | R18 bool `json:"r18"` 58 | Width int `json:"width"` 59 | Height int `json:"height"` 60 | Tags []string `json:"tags"` 61 | Ext string `json:"ext"` 62 | Date int `json:"uploadDate"` 63 | Urls PicUrl `json:"urls"` 64 | DumpUrl string 65 | } 66 | 67 | type BotMsgType string 68 | 69 | const ( 70 | BotMsgText BotMsgType = "text" 71 | BotMsgNews BotMsgType = "news" 72 | BotMsgImage BotMsgType = "image" 73 | ) 74 | 75 | type BotMsgReq struct { 76 | MsgType BotMsgType `json:"msgtype"` 77 | News *News `json:"news,omitempty"` 78 | Image *Image `json:"image,omitempty"` 79 | Text *Text `json:"text,omitempty"` 80 | } 81 | 82 | type News struct { 83 | Articles []Article `json:"articles"` 84 | } 85 | 86 | type Article struct { 87 | Title string `json:"title"` 88 | Description string `json:"description"` 89 | Url string `json:"url"` 90 | Picurl string `json:"picurl"` 91 | } 92 | 93 | type Image struct { 94 | Base64 string `json:"base64"` 95 | Md5 string `json:"md5"` 96 | } 97 | 98 | type Text struct { 99 | Content string `json:"content"` 100 | MentionedList []string `json:"mentioned_list"` 101 | } 102 | -------------------------------------------------------------------------------- /transmit/msgforward.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.26.0 4 | // protoc v3.19.1 5 | // source: msgforward.proto 6 | 7 | package transmit 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 15 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 16 | reflect "reflect" 17 | sync "sync" 18 | ) 19 | 20 | const ( 21 | // Verify that this generated code is sufficiently up-to-date. 22 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 23 | // Verify that runtime/protoimpl is sufficiently up-to-date. 24 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 25 | ) 26 | 27 | type SeTuRequest struct { 28 | state protoimpl.MessageState 29 | sizeCache protoimpl.SizeCache 30 | unknownFields protoimpl.UnknownFields 31 | 32 | Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` 33 | Desc string `protobuf:"bytes,2,opt,name=desc,proto3" json:"desc,omitempty"` 34 | OriginalUrl string `protobuf:"bytes,3,opt,name=originalUrl,proto3" json:"originalUrl,omitempty"` 35 | Url string `protobuf:"bytes,4,opt,name=url,proto3" json:"url,omitempty"` 36 | PicBase64 string `protobuf:"bytes,5,opt,name=picBase64,proto3" json:"picBase64,omitempty"` 37 | PicMd5 string `protobuf:"bytes,6,opt,name=picMd5,proto3" json:"picMd5,omitempty"` 38 | } 39 | 40 | func (x *SeTuRequest) Reset() { 41 | *x = SeTuRequest{} 42 | if protoimpl.UnsafeEnabled { 43 | mi := &file_msgforward_proto_msgTypes[0] 44 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 45 | ms.StoreMessageInfo(mi) 46 | } 47 | } 48 | 49 | func (x *SeTuRequest) String() string { 50 | return protoimpl.X.MessageStringOf(x) 51 | } 52 | 53 | func (*SeTuRequest) ProtoMessage() {} 54 | 55 | func (x *SeTuRequest) ProtoReflect() protoreflect.Message { 56 | mi := &file_msgforward_proto_msgTypes[0] 57 | if protoimpl.UnsafeEnabled && x != nil { 58 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 59 | if ms.LoadMessageInfo() == nil { 60 | ms.StoreMessageInfo(mi) 61 | } 62 | return ms 63 | } 64 | return mi.MessageOf(x) 65 | } 66 | 67 | // Deprecated: Use SeTuRequest.ProtoReflect.Descriptor instead. 68 | func (*SeTuRequest) Descriptor() ([]byte, []int) { 69 | return file_msgforward_proto_rawDescGZIP(), []int{0} 70 | } 71 | 72 | func (x *SeTuRequest) GetTitle() string { 73 | if x != nil { 74 | return x.Title 75 | } 76 | return "" 77 | } 78 | 79 | func (x *SeTuRequest) GetDesc() string { 80 | if x != nil { 81 | return x.Desc 82 | } 83 | return "" 84 | } 85 | 86 | func (x *SeTuRequest) GetOriginalUrl() string { 87 | if x != nil { 88 | return x.OriginalUrl 89 | } 90 | return "" 91 | } 92 | 93 | func (x *SeTuRequest) GetUrl() string { 94 | if x != nil { 95 | return x.Url 96 | } 97 | return "" 98 | } 99 | 100 | func (x *SeTuRequest) GetPicBase64() string { 101 | if x != nil { 102 | return x.PicBase64 103 | } 104 | return "" 105 | } 106 | 107 | func (x *SeTuRequest) GetPicMd5() string { 108 | if x != nil { 109 | return x.PicMd5 110 | } 111 | return "" 112 | } 113 | 114 | type SeTuReply struct { 115 | state protoimpl.MessageState 116 | sizeCache protoimpl.SizeCache 117 | unknownFields protoimpl.UnknownFields 118 | 119 | ErrMessage string `protobuf:"bytes,1,opt,name=errMessage,proto3" json:"errMessage,omitempty"` 120 | } 121 | 122 | func (x *SeTuReply) Reset() { 123 | *x = SeTuReply{} 124 | if protoimpl.UnsafeEnabled { 125 | mi := &file_msgforward_proto_msgTypes[1] 126 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 127 | ms.StoreMessageInfo(mi) 128 | } 129 | } 130 | 131 | func (x *SeTuReply) String() string { 132 | return protoimpl.X.MessageStringOf(x) 133 | } 134 | 135 | func (*SeTuReply) ProtoMessage() {} 136 | 137 | func (x *SeTuReply) ProtoReflect() protoreflect.Message { 138 | mi := &file_msgforward_proto_msgTypes[1] 139 | if protoimpl.UnsafeEnabled && x != nil { 140 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 141 | if ms.LoadMessageInfo() == nil { 142 | ms.StoreMessageInfo(mi) 143 | } 144 | return ms 145 | } 146 | return mi.MessageOf(x) 147 | } 148 | 149 | // Deprecated: Use SeTuReply.ProtoReflect.Descriptor instead. 150 | func (*SeTuReply) Descriptor() ([]byte, []int) { 151 | return file_msgforward_proto_rawDescGZIP(), []int{1} 152 | } 153 | 154 | func (x *SeTuReply) GetErrMessage() string { 155 | if x != nil { 156 | return x.ErrMessage 157 | } 158 | return "" 159 | } 160 | 161 | var File_msgforward_proto protoreflect.FileDescriptor 162 | 163 | var file_msgforward_proto_rawDesc = []byte{ 164 | 0x0a, 0x10, 0x6d, 0x73, 0x67, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 165 | 0x74, 0x6f, 0x12, 0x08, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x22, 0xa1, 0x01, 0x0a, 166 | 0x0b, 0x53, 0x65, 0x54, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 167 | 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 168 | 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 169 | 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 170 | 0x61, 0x6c, 0x55, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x72, 0x69, 171 | 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x55, 0x72, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 172 | 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x69, 173 | 0x63, 0x42, 0x61, 0x73, 0x65, 0x36, 0x34, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 174 | 0x69, 0x63, 0x42, 0x61, 0x73, 0x65, 0x36, 0x34, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x69, 0x63, 0x4d, 175 | 0x64, 0x35, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x69, 0x63, 0x4d, 0x64, 0x35, 176 | 0x22, 0x2b, 0x0a, 0x09, 0x53, 0x65, 0x54, 0x75, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1e, 0x0a, 177 | 0x0a, 0x65, 0x72, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 178 | 0x09, 0x52, 0x0a, 0x65, 0x72, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x47, 0x0a, 179 | 0x0b, 0x53, 0x65, 0x74, 0x75, 0x43, 0x6f, 0x75, 0x72, 0x69, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x08, 180 | 0x53, 0x65, 0x6e, 0x64, 0x53, 0x75, 0x54, 0x75, 0x12, 0x15, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 181 | 0x6d, 0x69, 0x74, 0x2e, 0x53, 0x65, 0x54, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 182 | 0x13, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x2e, 0x53, 0x65, 0x54, 0x75, 0x52, 183 | 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2e, 0x2f, 0x74, 0x72, 0x61, 184 | 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 185 | } 186 | 187 | var ( 188 | file_msgforward_proto_rawDescOnce sync.Once 189 | file_msgforward_proto_rawDescData = file_msgforward_proto_rawDesc 190 | ) 191 | 192 | func file_msgforward_proto_rawDescGZIP() []byte { 193 | file_msgforward_proto_rawDescOnce.Do(func() { 194 | file_msgforward_proto_rawDescData = protoimpl.X.CompressGZIP(file_msgforward_proto_rawDescData) 195 | }) 196 | return file_msgforward_proto_rawDescData 197 | } 198 | 199 | var file_msgforward_proto_msgTypes = make([]protoimpl.MessageInfo, 2) 200 | var file_msgforward_proto_goTypes = []interface{}{ 201 | (*SeTuRequest)(nil), // 0: transmit.SeTuRequest 202 | (*SeTuReply)(nil), // 1: transmit.SeTuReply 203 | } 204 | var file_msgforward_proto_depIdxs = []int32{ 205 | 0, // 0: transmit.SetuCourier.SendSuTu:input_type -> transmit.SeTuRequest 206 | 1, // 1: transmit.SetuCourier.SendSuTu:output_type -> transmit.SeTuReply 207 | 1, // [1:2] is the sub-list for method output_type 208 | 0, // [0:1] is the sub-list for method input_type 209 | 0, // [0:0] is the sub-list for extension type_name 210 | 0, // [0:0] is the sub-list for extension extendee 211 | 0, // [0:0] is the sub-list for field type_name 212 | } 213 | 214 | func init() { file_msgforward_proto_init() } 215 | func file_msgforward_proto_init() { 216 | if File_msgforward_proto != nil { 217 | return 218 | } 219 | if !protoimpl.UnsafeEnabled { 220 | file_msgforward_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 221 | switch v := v.(*SeTuRequest); i { 222 | case 0: 223 | return &v.state 224 | case 1: 225 | return &v.sizeCache 226 | case 2: 227 | return &v.unknownFields 228 | default: 229 | return nil 230 | } 231 | } 232 | file_msgforward_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 233 | switch v := v.(*SeTuReply); i { 234 | case 0: 235 | return &v.state 236 | case 1: 237 | return &v.sizeCache 238 | case 2: 239 | return &v.unknownFields 240 | default: 241 | return nil 242 | } 243 | } 244 | } 245 | type x struct{} 246 | out := protoimpl.TypeBuilder{ 247 | File: protoimpl.DescBuilder{ 248 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 249 | RawDescriptor: file_msgforward_proto_rawDesc, 250 | NumEnums: 0, 251 | NumMessages: 2, 252 | NumExtensions: 0, 253 | NumServices: 1, 254 | }, 255 | GoTypes: file_msgforward_proto_goTypes, 256 | DependencyIndexes: file_msgforward_proto_depIdxs, 257 | MessageInfos: file_msgforward_proto_msgTypes, 258 | }.Build() 259 | File_msgforward_proto = out.File 260 | file_msgforward_proto_rawDesc = nil 261 | file_msgforward_proto_goTypes = nil 262 | file_msgforward_proto_depIdxs = nil 263 | } 264 | 265 | // Reference imports to suppress errors if they are not otherwise used. 266 | var _ context.Context 267 | var _ grpc.ClientConnInterface 268 | 269 | // This is a compile-time assertion to ensure that this generated file 270 | // is compatible with the grpc package it is being compiled against. 271 | const _ = grpc.SupportPackageIsVersion6 272 | 273 | // SetuCourierClient is the client API for SetuCourier service. 274 | // 275 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 276 | type SetuCourierClient interface { 277 | SendSuTu(ctx context.Context, in *SeTuRequest, opts ...grpc.CallOption) (*SeTuReply, error) 278 | } 279 | 280 | type setuCourierClient struct { 281 | cc grpc.ClientConnInterface 282 | } 283 | 284 | func NewSetuCourierClient(cc grpc.ClientConnInterface) SetuCourierClient { 285 | return &setuCourierClient{cc} 286 | } 287 | 288 | func (c *setuCourierClient) SendSuTu(ctx context.Context, in *SeTuRequest, opts ...grpc.CallOption) (*SeTuReply, error) { 289 | out := new(SeTuReply) 290 | err := c.cc.Invoke(ctx, "/transmit.SetuCourier/SendSuTu", in, out, opts...) 291 | if err != nil { 292 | return nil, err 293 | } 294 | return out, nil 295 | } 296 | 297 | // SetuCourierServer is the server API for SetuCourier service. 298 | type SetuCourierServer interface { 299 | SendSuTu(context.Context, *SeTuRequest) (*SeTuReply, error) 300 | } 301 | 302 | // UnimplementedSetuCourierServer can be embedded to have forward compatible implementations. 303 | type UnimplementedSetuCourierServer struct { 304 | } 305 | 306 | func (*UnimplementedSetuCourierServer) SendSuTu(context.Context, *SeTuRequest) (*SeTuReply, error) { 307 | return nil, status.Errorf(codes.Unimplemented, "method SendSuTu not implemented") 308 | } 309 | 310 | func RegisterSetuCourierServer(s *grpc.Server, srv SetuCourierServer) { 311 | s.RegisterService(&_SetuCourier_serviceDesc, srv) 312 | } 313 | 314 | func _SetuCourier_SendSuTu_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 315 | in := new(SeTuRequest) 316 | if err := dec(in); err != nil { 317 | return nil, err 318 | } 319 | if interceptor == nil { 320 | return srv.(SetuCourierServer).SendSuTu(ctx, in) 321 | } 322 | info := &grpc.UnaryServerInfo{ 323 | Server: srv, 324 | FullMethod: "/transmit.SetuCourier/SendSuTu", 325 | } 326 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 327 | return srv.(SetuCourierServer).SendSuTu(ctx, req.(*SeTuRequest)) 328 | } 329 | return interceptor(ctx, in, info, handler) 330 | } 331 | 332 | var _SetuCourier_serviceDesc = grpc.ServiceDesc{ 333 | ServiceName: "transmit.SetuCourier", 334 | HandlerType: (*SetuCourierServer)(nil), 335 | Methods: []grpc.MethodDesc{ 336 | { 337 | MethodName: "SendSuTu", 338 | Handler: _SetuCourier_SendSuTu_Handler, 339 | }, 340 | }, 341 | Streams: []grpc.StreamDesc{}, 342 | Metadata: "msgforward.proto", 343 | } 344 | -------------------------------------------------------------------------------- /transmit/picdump.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.26.0 4 | // protoc v3.19.1 5 | // source: picdump.proto 6 | 7 | package transmit 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 15 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 16 | reflect "reflect" 17 | sync "sync" 18 | ) 19 | 20 | const ( 21 | // Verify that this generated code is sufficiently up-to-date. 22 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 23 | // Verify that runtime/protoimpl is sufficiently up-to-date. 24 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 25 | ) 26 | 27 | type PicRequest struct { 28 | state protoimpl.MessageState 29 | sizeCache protoimpl.SizeCache 30 | unknownFields protoimpl.UnknownFields 31 | 32 | Pic []byte `protobuf:"bytes,1,opt,name=pic,proto3" json:"pic,omitempty"` 33 | PicName string `protobuf:"bytes,2,opt,name=picName,proto3" json:"picName,omitempty"` 34 | } 35 | 36 | func (x *PicRequest) Reset() { 37 | *x = PicRequest{} 38 | if protoimpl.UnsafeEnabled { 39 | mi := &file_picdump_proto_msgTypes[0] 40 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 41 | ms.StoreMessageInfo(mi) 42 | } 43 | } 44 | 45 | func (x *PicRequest) String() string { 46 | return protoimpl.X.MessageStringOf(x) 47 | } 48 | 49 | func (*PicRequest) ProtoMessage() {} 50 | 51 | func (x *PicRequest) ProtoReflect() protoreflect.Message { 52 | mi := &file_picdump_proto_msgTypes[0] 53 | if protoimpl.UnsafeEnabled && x != nil { 54 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 55 | if ms.LoadMessageInfo() == nil { 56 | ms.StoreMessageInfo(mi) 57 | } 58 | return ms 59 | } 60 | return mi.MessageOf(x) 61 | } 62 | 63 | // Deprecated: Use PicRequest.ProtoReflect.Descriptor instead. 64 | func (*PicRequest) Descriptor() ([]byte, []int) { 65 | return file_picdump_proto_rawDescGZIP(), []int{0} 66 | } 67 | 68 | func (x *PicRequest) GetPic() []byte { 69 | if x != nil { 70 | return x.Pic 71 | } 72 | return nil 73 | } 74 | 75 | func (x *PicRequest) GetPicName() string { 76 | if x != nil { 77 | return x.PicName 78 | } 79 | return "" 80 | } 81 | 82 | type PicReply struct { 83 | state protoimpl.MessageState 84 | sizeCache protoimpl.SizeCache 85 | unknownFields protoimpl.UnknownFields 86 | 87 | Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` 88 | } 89 | 90 | func (x *PicReply) Reset() { 91 | *x = PicReply{} 92 | if protoimpl.UnsafeEnabled { 93 | mi := &file_picdump_proto_msgTypes[1] 94 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 95 | ms.StoreMessageInfo(mi) 96 | } 97 | } 98 | 99 | func (x *PicReply) String() string { 100 | return protoimpl.X.MessageStringOf(x) 101 | } 102 | 103 | func (*PicReply) ProtoMessage() {} 104 | 105 | func (x *PicReply) ProtoReflect() protoreflect.Message { 106 | mi := &file_picdump_proto_msgTypes[1] 107 | if protoimpl.UnsafeEnabled && x != nil { 108 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 109 | if ms.LoadMessageInfo() == nil { 110 | ms.StoreMessageInfo(mi) 111 | } 112 | return ms 113 | } 114 | return mi.MessageOf(x) 115 | } 116 | 117 | // Deprecated: Use PicReply.ProtoReflect.Descriptor instead. 118 | func (*PicReply) Descriptor() ([]byte, []int) { 119 | return file_picdump_proto_rawDescGZIP(), []int{1} 120 | } 121 | 122 | func (x *PicReply) GetMessage() string { 123 | if x != nil { 124 | return x.Message 125 | } 126 | return "" 127 | } 128 | 129 | var File_picdump_proto protoreflect.FileDescriptor 130 | 131 | var file_picdump_proto_rawDesc = []byte{ 132 | 0x0a, 0x0d, 0x70, 0x69, 0x63, 0x64, 0x75, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 133 | 0x08, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x22, 0x38, 0x0a, 0x0a, 0x50, 0x69, 0x63, 134 | 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x63, 0x18, 0x01, 135 | 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x70, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x69, 0x63, 136 | 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x69, 0x63, 0x4e, 137 | 0x61, 0x6d, 0x65, 0x22, 0x24, 0x0a, 0x08, 0x50, 0x69, 0x63, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 138 | 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 139 | 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x43, 0x0a, 0x0a, 0x50, 0x69, 0x63, 140 | 0x43, 0x6f, 0x75, 0x72, 0x69, 0x65, 0x72, 0x12, 0x35, 0x0a, 0x07, 0x53, 0x65, 0x6e, 0x64, 0x50, 141 | 0x69, 0x63, 0x12, 0x14, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x2e, 0x50, 0x69, 142 | 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 143 | 0x6d, 0x69, 0x74, 0x2e, 0x50, 0x69, 0x63, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x0d, 144 | 0x5a, 0x0b, 0x2e, 0x2e, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x62, 0x06, 0x70, 145 | 0x72, 0x6f, 0x74, 0x6f, 0x33, 146 | } 147 | 148 | var ( 149 | file_picdump_proto_rawDescOnce sync.Once 150 | file_picdump_proto_rawDescData = file_picdump_proto_rawDesc 151 | ) 152 | 153 | func file_picdump_proto_rawDescGZIP() []byte { 154 | file_picdump_proto_rawDescOnce.Do(func() { 155 | file_picdump_proto_rawDescData = protoimpl.X.CompressGZIP(file_picdump_proto_rawDescData) 156 | }) 157 | return file_picdump_proto_rawDescData 158 | } 159 | 160 | var file_picdump_proto_msgTypes = make([]protoimpl.MessageInfo, 2) 161 | var file_picdump_proto_goTypes = []interface{}{ 162 | (*PicRequest)(nil), // 0: transmit.PicRequest 163 | (*PicReply)(nil), // 1: transmit.PicReply 164 | } 165 | var file_picdump_proto_depIdxs = []int32{ 166 | 0, // 0: transmit.PicCourier.SendPic:input_type -> transmit.PicRequest 167 | 1, // 1: transmit.PicCourier.SendPic:output_type -> transmit.PicReply 168 | 1, // [1:2] is the sub-list for method output_type 169 | 0, // [0:1] is the sub-list for method input_type 170 | 0, // [0:0] is the sub-list for extension type_name 171 | 0, // [0:0] is the sub-list for extension extendee 172 | 0, // [0:0] is the sub-list for field type_name 173 | } 174 | 175 | func init() { file_picdump_proto_init() } 176 | func file_picdump_proto_init() { 177 | if File_picdump_proto != nil { 178 | return 179 | } 180 | if !protoimpl.UnsafeEnabled { 181 | file_picdump_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 182 | switch v := v.(*PicRequest); i { 183 | case 0: 184 | return &v.state 185 | case 1: 186 | return &v.sizeCache 187 | case 2: 188 | return &v.unknownFields 189 | default: 190 | return nil 191 | } 192 | } 193 | file_picdump_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 194 | switch v := v.(*PicReply); i { 195 | case 0: 196 | return &v.state 197 | case 1: 198 | return &v.sizeCache 199 | case 2: 200 | return &v.unknownFields 201 | default: 202 | return nil 203 | } 204 | } 205 | } 206 | type x struct{} 207 | out := protoimpl.TypeBuilder{ 208 | File: protoimpl.DescBuilder{ 209 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 210 | RawDescriptor: file_picdump_proto_rawDesc, 211 | NumEnums: 0, 212 | NumMessages: 2, 213 | NumExtensions: 0, 214 | NumServices: 1, 215 | }, 216 | GoTypes: file_picdump_proto_goTypes, 217 | DependencyIndexes: file_picdump_proto_depIdxs, 218 | MessageInfos: file_picdump_proto_msgTypes, 219 | }.Build() 220 | File_picdump_proto = out.File 221 | file_picdump_proto_rawDesc = nil 222 | file_picdump_proto_goTypes = nil 223 | file_picdump_proto_depIdxs = nil 224 | } 225 | 226 | // Reference imports to suppress errors if they are not otherwise used. 227 | var _ context.Context 228 | var _ grpc.ClientConnInterface 229 | 230 | // This is a compile-time assertion to ensure that this generated file 231 | // is compatible with the grpc package it is being compiled against. 232 | const _ = grpc.SupportPackageIsVersion6 233 | 234 | // PicCourierClient is the client API for PicCourier service. 235 | // 236 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 237 | type PicCourierClient interface { 238 | SendPic(ctx context.Context, in *PicRequest, opts ...grpc.CallOption) (*PicReply, error) 239 | } 240 | 241 | type picCourierClient struct { 242 | cc grpc.ClientConnInterface 243 | } 244 | 245 | func NewPicCourierClient(cc grpc.ClientConnInterface) PicCourierClient { 246 | return &picCourierClient{cc} 247 | } 248 | 249 | func (c *picCourierClient) SendPic(ctx context.Context, in *PicRequest, opts ...grpc.CallOption) (*PicReply, error) { 250 | out := new(PicReply) 251 | err := c.cc.Invoke(ctx, "/transmit.PicCourier/SendPic", in, out, opts...) 252 | if err != nil { 253 | return nil, err 254 | } 255 | return out, nil 256 | } 257 | 258 | // PicCourierServer is the server API for PicCourier service. 259 | type PicCourierServer interface { 260 | SendPic(context.Context, *PicRequest) (*PicReply, error) 261 | } 262 | 263 | // UnimplementedPicCourierServer can be embedded to have forward compatible implementations. 264 | type UnimplementedPicCourierServer struct { 265 | } 266 | 267 | func (*UnimplementedPicCourierServer) SendPic(context.Context, *PicRequest) (*PicReply, error) { 268 | return nil, status.Errorf(codes.Unimplemented, "method SendPic not implemented") 269 | } 270 | 271 | func RegisterPicCourierServer(s *grpc.Server, srv PicCourierServer) { 272 | s.RegisterService(&_PicCourier_serviceDesc, srv) 273 | } 274 | 275 | func _PicCourier_SendPic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 276 | in := new(PicRequest) 277 | if err := dec(in); err != nil { 278 | return nil, err 279 | } 280 | if interceptor == nil { 281 | return srv.(PicCourierServer).SendPic(ctx, in) 282 | } 283 | info := &grpc.UnaryServerInfo{ 284 | Server: srv, 285 | FullMethod: "/transmit.PicCourier/SendPic", 286 | } 287 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 288 | return srv.(PicCourierServer).SendPic(ctx, req.(*PicRequest)) 289 | } 290 | return interceptor(ctx, in, info, handler) 291 | } 292 | 293 | var _PicCourier_serviceDesc = grpc.ServiceDesc{ 294 | ServiceName: "transmit.PicCourier", 295 | HandlerType: (*PicCourierServer)(nil), 296 | Methods: []grpc.MethodDesc{ 297 | { 298 | MethodName: "SendPic", 299 | Handler: _PicCourier_SendPic_Handler, 300 | }, 301 | }, 302 | Streams: []grpc.StreamDesc{}, 303 | Metadata: "picdump.proto", 304 | } 305 | --------------------------------------------------------------------------------