├── .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 |

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 |

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 |
--------------------------------------------------------------------------------