├── .github
└── workflows
│ └── builder.yaml
├── .gitignore
├── Dockerfile
├── IPLocation
├── IpLocation.php
├── IpParserInterface.php
├── IpV6wry.php
├── QQwry.php
├── StringParser.php
├── ipv6wry.db
└── qqwry.dat
├── LICENSE
├── Makefile
├── README.md
├── basic-auth.php
├── bot.go
├── bot.service
├── config.json
├── go.mod
├── go.sum
├── pics
├── 1.png
├── 2.jpg
└── plugin.png
├── test.php
└── tgbot.php
/.github/workflows/builder.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - 'master'
5 |
6 | jobs:
7 | docker:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v2
12 | with:
13 | submodules: true
14 |
15 | - name: Set up QEMU
16 | uses: docker/setup-qemu-action@v1
17 |
18 | - name: Set up Docker Buildx
19 | uses: docker/setup-buildx-action@v1
20 |
21 | - name: Cache Docker layers
22 | uses: actions/cache@v2
23 | with:
24 | path: /tmp/.buildx-cache
25 | key: ${{ runner.os }}-buildx-${{ github.sha }}
26 | restore-keys: |
27 | ${{ runner.os }}-buildx-
28 |
29 | - name: Login to DockerHub
30 | uses: docker/login-action@v1
31 | with:
32 | username: ${{ secrets.DOCKERHUB_USERNAME }}
33 | password: ${{ secrets.DOCKERHUB_TOKEN }}
34 |
35 | - name: Lower case
36 | id: string
37 | uses: ASzc/change-string-case-action@v1
38 | with:
39 | string: ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}
40 |
41 | - name: Build and push
42 | uses: docker/build-push-action@v2
43 | with:
44 | context: .
45 | platforms: linux/arm,linux/amd64,linux/arm64
46 | push: true
47 | tags: ${{ steps.string.outputs.lowercase }}
48 | cache-from: type=local,src=/tmp/.buildx-cache
49 | cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
50 |
51 | - name: Move cache
52 | run: |
53 | rm -rf /tmp/.buildx-cache
54 | mv /tmp/.buildx-cache-new /tmp/.buildx-cache
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # ignore everything in the root except the "wp-content" directory.
2 | !wp-content/
3 |
4 | # ignore everything in the "wp-content" directory, except:
5 | # "mu-plugins", "plugins", "themes" directory
6 | wp-content/*
7 | !wp-content/mu-plugins/
8 | !wp-content/plugins/
9 | !wp-content/themes/
10 |
11 | # ignore these plugins
12 | wp-content/plugins/hello.php
13 |
14 | # ignore specific themes
15 | wp-content/themes/twenty*/
16 |
17 | # ignore node dependency directories
18 | node_modules/
19 |
20 | # ignore log files and databases
21 | *.log
22 | *.sql
23 | *.sqlite
24 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.19.0-alpine as builder
2 |
3 | RUN apk update && apk add --no-cache make ca-certificates && mkdir /build
4 | COPY go.mod /build
5 | RUN cd /build && go mod download
6 | COPY . /build
7 | RUN cd /build && make static
8 |
9 |
10 | FROM scratch
11 |
12 | COPY --from=builder /build/comments /comments
13 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
14 | WORKDIR /
15 |
16 | ENTRYPOINT ["/comments","-c","/config.json"]
17 |
18 | # docker run -d --restart=always-v ./config.json:/config.json bennythink/archiver
19 |
--------------------------------------------------------------------------------
/IPLocation/IpLocation.php:
--------------------------------------------------------------------------------
1 | setDBPath(self::getIpV4Path());
48 | $location = $ins->getIp($ip);
49 | } else if (self::isIpV6($ip)) {
50 | $ins = new IpV6wry();
51 | $ins->setDBPath(self::getIpV6Path());
52 | $location = $ins->getIp($ip);
53 |
54 | } else {
55 | $location = [
56 | 'error' => 'IP Invalid'
57 | ];
58 | }
59 |
60 | return $location;
61 | }
62 |
63 | /**
64 | * @param $ip
65 | * @param string $ipV4Path
66 | * @param string $ipV6Path
67 | * @return array|mixed
68 | */
69 | public static function getLocation($ip, $ipV4Path='', $ipV6Path='') {
70 | $location = self::getLocationWithoutParse($ip, $ipV4Path, $ipV6Path);
71 | if (isset($location['error'])) {
72 | return $location;
73 | }
74 | return StringParser::parse($location);
75 | }
76 |
77 | /**
78 | * @param $path
79 | */
80 | public static function setIpV4Path($path)
81 | {
82 | self::$ipV4Path = $path;
83 | }
84 |
85 | /**
86 | * @param $path
87 | */
88 | public static function setIpV6Path($path)
89 | {
90 | self::$ipV6Path = $path;
91 | }
92 |
93 | /**
94 | * @return string
95 | */
96 | private static function getIpV4Path() {
97 | return self::$ipV4Path ? : self::src('/qqwry.dat');
98 | }
99 |
100 | /**
101 | * @return string
102 | */
103 | private static function getIpV6Path() {
104 | return self::$ipV6Path ? : self::src('/ipv6wry.db');
105 | }
106 |
107 | /**
108 | * @param $ip
109 | * @return bool
110 | */
111 | private static function isIpV4($ip) {
112 | return false !== filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
113 | }
114 |
115 | /**
116 | * @param $ip
117 | * @return bool
118 | */
119 | private static function isIpV6($ip) {
120 | return false !== filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
121 | }
122 |
123 | /**
124 | * @param $filename
125 | * @return string
126 | */
127 | public static function src($filename) {
128 | return self::root('/IPLocation'.$filename);
129 | }
130 |
131 | /**
132 | * @param $filename
133 | * @return string
134 | */
135 | public static function root($filename) {
136 | return IP_DATABASE_ROOT_DIR . $filename;
137 | }
138 | }
--------------------------------------------------------------------------------
/IPLocation/IpParserInterface.php:
--------------------------------------------------------------------------------
1 | $exception->getMessage(),
29 | ];
30 | }
31 |
32 | $return = [
33 | 'ip' => $ip,
34 | 'country' => $tmp['addr'][0],
35 | 'area' => $tmp['addr'][1],
36 | ];
37 | return $return;
38 | }
39 |
40 |
41 | private static $filePath;
42 |
43 | const FORMAT = 'J2';
44 | private static $total = null;
45 | // 索引区
46 | private static $index_start_offset;
47 | private static $index_end_offset;
48 | private static $offlen;
49 | private static $iplen;
50 | private static $has_initialized = false;
51 |
52 | /**
53 | * return database record count
54 | * @return int|string
55 | */
56 | public static function total()
57 | {
58 | if (null === static::$total) {
59 | $fd = fopen(static::$filePath, 'rb');
60 | static::initialize($fd);
61 | fclose($fd);
62 | }
63 | return static::$total;
64 | }
65 |
66 | public static function initialize($fd)
67 | {
68 | if (!static::$has_initialized) {
69 | if (PHP_INT_SIZE < 8) {
70 | throw new \RuntimeException('64bit OS supported only');
71 | }
72 | if (version_compare(PHP_VERSION, "7.0", "<")) {
73 | throw new \RuntimeException('php version 7.0 or greater');
74 | }
75 | static::$index_start_offset = static::read8($fd, 16);
76 | static::$offlen = static::read1($fd, 6);
77 | static::$iplen = static::read1($fd, 7);
78 | static::$total = static::read8($fd, 8);
79 | static::$index_end_offset = static::$index_start_offset
80 | + (static::$iplen + static::$offlen) * static::$total;
81 | static::$has_initialized = true;
82 | }
83 | }
84 |
85 | /**
86 | * query ipv6
87 | * @param $ip
88 | * @return array
89 | */
90 | public static function query($ip)
91 | {
92 | $ip_bin = inet_pton($ip);
93 | if (false === $ip_bin) {
94 | throw new \RuntimeException("error IPv6 address: $ip");
95 | }
96 | if (16 !== strlen($ip_bin)) {
97 | throw new \RuntimeException("error IPv6 address: $ip");
98 | }
99 | $fd = fopen(static::$filePath, 'rb');
100 | static::initialize($fd);
101 | $ip_num_arr = unpack(static::FORMAT, $ip_bin);
102 | // IP地址前半部分转换成有int
103 | $ip_num1 = $ip_num_arr[1];
104 | // IP地址后半部分转换成有int
105 | $ip_num2 = $ip_num_arr[2];
106 | $ip_find = static::find($fd, $ip_num1, $ip_num2, 0, static::$total);
107 | $ip_offset = static::$index_start_offset + $ip_find * (static::$iplen + static::$offlen);
108 | $ip_offset2 = $ip_offset + static::$iplen + static::$offlen;
109 | $ip_start = inet_ntop(pack(static::FORMAT, static::read8($fd, $ip_offset), 0));
110 | try {
111 | $ip_end = inet_ntop(pack(static::FORMAT, static::read8($fd, $ip_offset2) - 1, 0));
112 | } catch (\RuntimeException $e) {
113 | $ip_end = "FFFF:FFFF:FFFF:FFFF::";
114 | }
115 | $ip_record_offset = static::read8($fd, $ip_offset + static::$iplen, static::$offlen);
116 | $ip_addr = static::read_record($fd, $ip_record_offset);
117 | $ip_addr_disp = $ip_addr[0] . " " . $ip_addr[1];
118 | if (is_resource($fd)) {
119 | fclose($fd);
120 | }
121 | return ["start" => $ip_start, "end" => $ip_end, "addr" => $ip_addr, "disp" => $ip_addr_disp];
122 | }
123 |
124 | /**
125 | * 读取记录
126 | * @param $fd
127 | * @param $offset
128 | * @return string[]
129 | */
130 | public static function read_record($fd, $offset)
131 | {
132 | $record = [0 => "", 1 => ""];
133 | $flag = static::read1($fd, $offset);
134 | if ($flag == 1) {
135 | $location_offset = static::read8($fd, $offset + 1, static::$offlen);
136 | return static::read_record($fd, $location_offset);
137 | }
138 | $record[0] = static::read_location($fd, $offset);
139 | if ($flag == 2) {
140 | $record[1] = static::read_location($fd, $offset + static::$offlen + 1);
141 | } else {
142 | $record[1] = static::read_location($fd, $offset + strlen($record[0]) + 1);
143 | }
144 | return $record;
145 | }
146 |
147 | /**
148 | * 读取地区
149 | * @param $fd
150 | * @param $offset
151 | * @return string
152 | */
153 | public static function read_location($fd, $offset)
154 | {
155 | if ($offset == 0) {
156 | return "";
157 | }
158 | $flag = static::read1($fd, $offset);
159 | // 出错
160 | if ($flag == 0) {
161 | return "";
162 | }
163 | // 仍然为重定向
164 | if ($flag == 2) {
165 | $offset = static::read8($fd, $offset + 1, static::$offlen);
166 | return static::read_location($fd, $offset);
167 | }
168 | return static::readstr($fd, $offset);
169 | }
170 |
171 | /**
172 | * 查找 ip 所在的索引
173 | * @param $fd
174 | * @param $ip_num1
175 | * @param $ip_num2
176 | * @param $l
177 | * @param $r
178 | * @return mixed
179 | */
180 | public static function find($fd, $ip_num1, $ip_num2, $l, $r)
181 | {
182 | if ($l + 1 >= $r) {
183 | return $l;
184 | }
185 | $m = intval(($l + $r) / 2);
186 | $m_ip1 = static::read8($fd, static::$index_start_offset + $m * (static::$iplen + static::$offlen),
187 | static::$iplen);
188 | $m_ip2 = 0;
189 | if (static::$iplen <= 8) {
190 | $m_ip1 <<= 8 * (8 - static::$iplen);
191 | } else {
192 | $m_ip2 = static::read8($fd, static::$index_start_offset + $m * (static::$iplen + static::$offlen) + 8,
193 | static::$iplen - 8);
194 | $m_ip2 <<= 8 * (16 - static::$iplen);
195 | }
196 | if (static::uint64cmp($ip_num1, $m_ip1) < 0) {
197 | return static::find($fd, $ip_num1, $ip_num2, $l, $m);
198 | }
199 | if (static::uint64cmp($ip_num1, $m_ip1) > 0) {
200 | return static::find($fd, $ip_num1, $ip_num2, $m, $r);
201 | }
202 | if (static::uint64cmp($ip_num2, $m_ip2) < 0) {
203 | return static::find($fd, $ip_num1, $ip_num2, $l, $m);
204 | }
205 | return static::find($fd, $ip_num1, $ip_num2, $m, $r);
206 | }
207 |
208 | public static function readraw($fd, $offset = null, $size = 0)
209 | {
210 | if (!is_null($offset)) {
211 | fseek($fd, $offset);
212 | }
213 | return fread($fd, $size);
214 | }
215 |
216 | public static function read1($fd, $offset = null)
217 | {
218 | if (!is_null($offset)) {
219 | fseek($fd, $offset);
220 | }
221 | $a = fread($fd, 1);
222 | return @unpack("C", $a)[1];
223 | }
224 |
225 | public static function read8($fd, $offset = null, $size = 8)
226 | {
227 | if (!is_null($offset)) {
228 | fseek($fd, $offset);
229 | }
230 | $a = fread($fd, $size) . "\0\0\0\0\0\0\0\0";
231 | return @unpack("P", $a)[1];
232 | }
233 |
234 | public static function readstr($fd, $offset = null)
235 | {
236 | if (!is_null($offset)) {
237 | fseek($fd, $offset);
238 | }
239 | $str = "";
240 | $chr = static::read1($fd, $offset);
241 | while ($chr != 0) {
242 | $str .= chr($chr);
243 | $offset++;
244 | $chr = static::read1($fd, $offset);
245 | }
246 | return $str;
247 | }
248 |
249 | public static function ip2num($ip)
250 | {
251 | return unpack("N", inet_pton($ip))[1];
252 | }
253 |
254 | public static function inet_ntoa($nip)
255 | {
256 | $ip = [];
257 | for ($i = 3; $i > 0; $i--) {
258 | $ip_seg = intval($nip / pow(256, $i));
259 | $ip[] = $ip_seg;
260 | $nip -= $ip_seg * pow(256, $i);
261 | }
262 | $ip[] = $nip;
263 | return join(".", $ip);
264 | }
265 |
266 | public static function uint64cmp($a, $b)
267 | {
268 | if ($a >= 0 && $b >= 0 || $a < 0 && $b < 0) {
269 | return $a <=> $b;
270 | }
271 | if ($a >= 0 && $b < 0) {
272 | return -1;
273 | }
274 | return 1;
275 | }
276 |
277 | }
278 |
--------------------------------------------------------------------------------
/IPLocation/QQwry.php:
--------------------------------------------------------------------------------
1 | filePath = $filePath;
17 | }
18 |
19 | /**
20 | * @param $ip
21 | * @return array
22 | */
23 | public function getIp($ip)
24 | {
25 | try {
26 | $tmp = $this->getAddr($ip);
27 | } catch (\Exception $exception) {
28 | return [
29 | 'error' => $exception->getMessage(),
30 | ];
31 | }
32 |
33 | $return = [
34 | 'ip' => $ip,
35 | 'country' => $tmp['country'],
36 | 'area' => $tmp['area'],
37 | ];
38 | return $return;
39 | }
40 |
41 | /**
42 | * 文件路径
43 | * @var string
44 | */
45 | private $filePath;
46 | /**
47 | * qqwry.dat文件指针
48 | *
49 | * @var resource
50 | */
51 | private $fp;
52 | /**
53 | * 第一条IP记录的偏移地址
54 | *
55 | * @var int
56 | */
57 | private $firstIp;
58 | /**
59 | * 最后一条IP记录的偏移地址
60 | *
61 | * @var int
62 | */
63 | private $lastIp;
64 | /**
65 | * IP记录的总条数(不包含版本信息记录)
66 | *
67 | * @var int
68 | */
69 | private $totalIp;
70 |
71 |
72 | /**
73 | * 如果ip错误
74 | *
75 | * $result 是返回的数组
76 | * $result['ip'] 输入的ip
77 | * $result['country'] 国家 如 中国
78 | * $result['area'] 最完整的信息 如 中国河北省邢台市威县新科网吧(北外街)
79 | *
80 | *
81 | * @param $ip
82 | * @return array
83 | */
84 | public function getAddr($ip)
85 | {
86 | $filename = $this->filePath;
87 | if (!file_exists($filename)) {
88 | trigger_error("Failed open ip database file!");
89 | throw new \Exception('Failed open ip database');
90 | }
91 | if (is_null($this->fp)) {
92 | $this->fp = 0;
93 | if (($this->fp = fopen($filename, 'rb')) !== false) {
94 | $this->firstIp = $this->getLong();
95 | $this->lastIp = $this->getLong();
96 | $this->totalIp = ($this->lastIp - $this->firstIp) / 7;
97 | }
98 | }
99 | $location = $this->getLocation($ip);
100 | return $location;
101 | }
102 |
103 | /**
104 | * 返回读取的长整型数
105 | *
106 | * @access private
107 | * @return int
108 | */
109 | private function getLong()
110 | {
111 | //将读取的little-endian编码的4个字节转化为长整型数
112 | $result = unpack('Vlong', fread($this->fp, 4));
113 | return $result['long'];
114 | }
115 |
116 | /**
117 | * 根据所给 IP 地址或域名返回所在地区信息
118 | *
119 | * @access public
120 | * @param string $ip
121 | * @return array ip country area beginip endip
122 | */
123 | private function getLocation($ip)
124 | {
125 | if (!$this->fp) {
126 | return null;
127 | }
128 | // 如果数据文件没有被正确打开,则直接返回空
129 | $location['ip'] = $ip;
130 | $ip = $this->packIp($location['ip']);
131 | // 将输入的IP地址转化为可比较的IP地址
132 | // 不合法的IP地址会被转化为255.255.255.255
133 | // 对分搜索
134 | $l = 0;
135 | // 搜索的下边界
136 | $u = $this->totalIp;
137 | // 搜索的上边界
138 | $findip = $this->lastIp;
139 | // 如果没有找到就返回最后一条IP记录(qqwry.dat的版本信息)
140 | while ($l <= $u) {
141 | // 当上边界小于下边界时,查找失败
142 | $i = floor(($l + $u) / 2);
143 | // 计算近似中间记录
144 | fseek($this->fp, $this->firstIp + $i * 7);
145 | $beginip = strrev(fread($this->fp, 4));
146 | // 获取中间记录的开始IP地址
147 | // strrev函数在这里的作用是将little-endian的压缩IP地址转化为big-endian的格式
148 | // 以便用于比较,后面相同。
149 | if ($ip < $beginip) {
150 | // 用户的IP小于中间记录的开始IP地址时
151 | $u = $i - 1;
152 | // 将搜索的上边界修改为中间记录减一
153 | } else {
154 | fseek($this->fp, $this->getLong3());
155 | $endip = strrev(fread($this->fp, 4));
156 | // 获取中间记录的结束IP地址
157 | if ($ip > $endip) {
158 | // 用户的IP大于中间记录的结束IP地址时
159 | $l = $i + 1;
160 | // 将搜索的下边界修改为中间记录加一
161 | } else {
162 | // 用户的IP在中间记录的IP范围内时
163 | $findip = $this->firstIp + $i * 7;
164 | break;
165 | // 则表示找到结果,退出循环
166 | }
167 | }
168 | }
169 | //获取查找到的IP地理位置信息
170 | fseek($this->fp, $findip);
171 | $location['beginip'] = long2ip($this->getLong());
172 | // 用户IP所在范围的开始地址
173 | $offset = $this->getLong3();
174 | fseek($this->fp, $offset);
175 | $location['endip'] = long2ip($this->getLong());
176 | // 用户IP所在范围的结束地址
177 | $byte = fread($this->fp, 1);
178 | // 标志字节
179 | switch (ord($byte)) {
180 | case 1: // 标志字节为1,表示国家和区域信息都被同时重定向
181 | $countryOffset = $this->getLong3();
182 | // 重定向地址
183 | fseek($this->fp, $countryOffset);
184 | $byte = fread($this->fp, 1);
185 | // 标志字节
186 | switch (ord($byte)) {
187 | case 2: // 标志字节为2,表示国家信息被重定向
188 | fseek($this->fp, $this->getLong3());
189 | $location['country'] = $this->getString();
190 | fseek($this->fp, $countryOffset + 4);
191 | $location['area'] = $this->getArea();
192 | break;
193 | default: // 否则,表示国家信息没有被重定向
194 | $location['country'] = $this->getString($byte);
195 | $location['area'] = $this->getArea();
196 | break;
197 | }
198 | break;
199 | case 2: // 标志字节为2,表示国家信息被重定向
200 | fseek($this->fp, $this->getLong3());
201 | $location['country'] = $this->getString();
202 | fseek($this->fp, $offset + 8);
203 | $location['area'] = $this->getArea();
204 | break;
205 | default: // 否则,表示国家信息没有被重定向
206 | $location['country'] = $this->getString($byte);
207 | $location['area'] = $this->getArea();
208 | break;
209 | }
210 | $location['country'] = iconv("GBK", "UTF-8", $location['country']);
211 | $location['area'] = iconv("GBK", "UTF-8", $location['area']);
212 | if ($location['country'] == " CZ88.NET" || $location['country'] == "纯真网络") {
213 | // CZ88.NET表示没有有效信息
214 | $location['country'] = "无数据";
215 | }
216 | if ($location['area'] == " CZ88.NET") {
217 | $location['area'] = "";
218 | }
219 | return $location;
220 | }
221 |
222 | /**
223 | * 返回压缩后可进行比较的IP地址
224 | *
225 | * @access private
226 | * @param string $ip
227 | * @return string
228 | */
229 | private function packIp($ip)
230 | {
231 | // 将IP地址转化为长整型数,如果在PHP5中,IP地址错误,则返回False,
232 | // 这时intval将Flase转化为整数-1,之后压缩成big-endian编码的字符串
233 | return pack('N', intval($this->ip2long($ip)));
234 | }
235 |
236 | /**
237 | * Ip 地址转为数字地址
238 | * php 的 ip2long 这个函数有问题
239 | * 133.205.0.0 ==>> 2244804608
240 | *
241 | * @param string $ip 要转换的 ip 地址
242 | * @return int 转换完成的数字
243 | */
244 | private function ip2long($ip)
245 | {
246 | $ip_arr = explode('.', $ip);
247 | $iplong = (16777216 * intval($ip_arr[0])) + (65536 * intval($ip_arr[1])) + (256 * intval($ip_arr[2])) + intval($ip_arr[3]);
248 | return $iplong;
249 | }
250 |
251 | /**
252 | * 返回读取的3个字节的长整型数
253 | *
254 | * @access private
255 | * @return int
256 | */
257 | private function getLong3()
258 | {
259 | //将读取的little-endian编码的3个字节转化为长整型数
260 | $result = unpack('Vlong', fread($this->fp, 3) . chr(0));
261 | return $result['long'];
262 | }
263 |
264 | /**
265 | * 返回读取的字符串
266 | *
267 | * @access private
268 | * @param string $data
269 | * @return string
270 | */
271 | private function getString($data = "")
272 | {
273 | $char = fread($this->fp, 1);
274 | while (ord($char) > 0) {
275 | // 字符串按照C格式保存,以\0结束
276 | $data .= $char;
277 | // 将读取的字符连接到给定字符串之后
278 | $char = fread($this->fp, 1);
279 | }
280 | return $data;
281 | }
282 |
283 | /**
284 | * 返回地区信息
285 | *
286 | * @access private
287 | * @return string
288 | */
289 | private function getArea()
290 | {
291 | $byte = fread($this->fp, 1);
292 | // 标志字节
293 | switch (ord($byte)) {
294 | case 0: // 没有区域信息
295 | $area = "";
296 | break;
297 | case 1:
298 | case 2: // 标志字节为1或2,表示区域信息被重定向
299 | fseek($this->fp, $this->getLong3());
300 | $area = $this->getString();
301 | break;
302 | default: // 否则,表示区域信息没有被重定向
303 | $area = $this->getString($byte);
304 | break;
305 | }
306 | return $area;
307 | }
308 |
309 | /**
310 | * 析构函数,用于在页面执行结束后自动关闭打开的文件。
311 | */
312 | public function __destruct()
313 | {
314 | if ($this->fp) {
315 | fclose($this->fp);
316 | }
317 | $this->fp = 0;
318 | }
319 | }
--------------------------------------------------------------------------------
/IPLocation/StringParser.php:
--------------------------------------------------------------------------------
1 | $value) {
141 |
142 | if (false !== strpos($location['country'], $value)) {
143 | $isChina = true;
144 | $location['province'] = $value;
145 |
146 | //直辖市
147 | if (in_array($value, self::$dictCityDirectly)) {
148 |
149 | //直辖市
150 | $_tmp_province = explode($value, $location['country']);
151 |
152 | //市辖区
153 | if (isset($_tmp_province[1])) {
154 |
155 | $_tmp_province[1] = self::lTrim($_tmp_province[1], $separatorCity);
156 |
157 |
158 | if (strpos($_tmp_province[1], $separatorDistrict) !== false) {
159 | $_tmp_qu = explode($separatorDistrict, $_tmp_province[1]);
160 |
161 | //解决 休息休息校区 变成城市区域
162 | $isHitBlackTail = false;
163 | foreach (self::$dictDistrictBlackTails as $blackTail) {
164 | //尾
165 | if (mb_substr($_tmp_qu[0], -mb_strlen($blackTail)) == $blackTail) {
166 | $isHitBlackTail = true;
167 | break;
168 | }
169 | }
170 |
171 | //校区,学区
172 | if ((!$isHitBlackTail) && mb_strlen($_tmp_qu[0]) < 5) {
173 | //有点尴尬
174 | $location['city'] = $_tmp_qu[0] . $separatorDistrict;
175 | }
176 | }
177 | }
178 | } else {
179 |
180 | //没有省份标志 只能替换
181 | $_tmp_city = str_replace($location['province'], '', $location['country']);
182 |
183 | //防止直辖市捣乱 上海市xxx区 =》 市xx区
184 | $_tmp_city = self::lTrim($_tmp_city, $separatorCity);
185 |
186 | //内蒙古 类型的 获取市县信息
187 | if (strpos($_tmp_city, $separatorCity) !== false) {
188 | //市
189 | $_tmp_city = explode($separatorCity, $_tmp_city);
190 |
191 | $location['city'] = $_tmp_city[0] . $separatorCity;
192 |
193 | //县
194 | if (isset($_tmp_city[1])) {
195 | if (strpos($_tmp_city[1], $separatorCounty) !== false) {
196 | $_tmp_county = explode($separatorCounty, $_tmp_city[1]);
197 | $location['county'] = $_tmp_county[0] . $separatorCounty;
198 | }
199 |
200 | //区
201 | if (!$location['county'] && strpos($_tmp_city[1], $separatorDistrict) !== false) {
202 | $_tmp_qu = explode($separatorDistrict, $_tmp_city[1]);
203 | $location['county'] = $_tmp_qu[0] . $separatorDistrict;
204 | }
205 | }
206 | }
207 | }
208 |
209 | break;
210 | }
211 | }
212 | }
213 |
214 | if ($isChina) {
215 | $location['country'] = '中国';
216 | }
217 |
218 |
219 | $result['ip'] = $location['ip'];
220 |
221 | $result['country'] = $location['country'];
222 | $result['province'] = $location['province'];
223 | $result['city'] = $location['city'];
224 | $result['county'] = $location['county'];
225 |
226 | $result['area'] = $location['country'] . $location['province'] . $location['city'] . $location['county'] . ' ' . $location['org_area'];
227 |
228 | $result['isp'] = self::getIsp($result['area']);
229 |
230 | if ($withOriginal) {
231 | $result['org'] = $org;
232 | }
233 |
234 | return $result;
235 | }
236 |
237 |
238 | /**
239 | * @param $str
240 | * @return string
241 | */
242 | private static function getIsp($str)
243 | {
244 | $ret = '';
245 |
246 | foreach (self::$dictIsp as $k => $v) {
247 | if (false !== strpos($str, $v)) {
248 | $ret = $v;
249 | break;
250 | }
251 | }
252 |
253 | return $ret;
254 | }
255 |
256 | private static function lTrim($word, $w) {
257 | $pos = mb_stripos($word, $w);
258 | if ($pos === 0) {
259 | $word = mb_substr($word, 1);
260 | }
261 | return $word;
262 | }
263 |
264 |
265 | }
--------------------------------------------------------------------------------
/IPLocation/ipv6wry.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tgbot-collection/wp-comments/133421305ebfc26546a43611ce498273902641c8/IPLocation/ipv6wry.db
--------------------------------------------------------------------------------
/IPLocation/qqwry.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tgbot-collection/wp-comments/133421305ebfc26546a43611ce498273902641c8/IPLocation/qqwry.dat
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | , 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | OS = darwin linux windows
2 | ARCH = amd64
3 | default:
4 | git pull
5 | @echo "Build current platform executable..."
6 | go build .
7 |
8 | static:
9 | @echo "Build static files..."
10 | CGO_ENABLED=0 go build -a -ldflags '-s -w -extldflags "-static"' -o comments .
11 |
12 | all:
13 | git pull
14 | @echo "Build all platform executables..."
15 | @for o in $(OS) ; do \
16 | for a in $(ARCH) ; do \
17 | CGO_ENABLED=0 GOOS=$$o GOARCH=$$a go build -ldflags="-s -w" -o builds/DailyGakki-$$o-$$a .; \
18 | done \
19 | done
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # wp-comments-tgbot
2 | A Telegram Bot for WordPress comments! Simple and fast reply to your comments!
3 | 
4 |
5 | 
6 |
7 | # Warning
8 | By using this plugin, you agree to open Basic Auth on your WordPress.
9 |
10 | Please setup a complex passphrase for your WordPress
11 |
12 | # Requirements
13 | 1. WordPress native comment system.
14 |
15 | 2. Network connection to Telegram.
16 |
17 | # General guide
18 | ## 1. Install plugin
19 | ssh to your blog
20 | ```shell script
21 | cd /path/to/root/wp-content/plugins
22 | git clone https://github.com/BennyThink/wp-comments-tgbot
23 | ```
24 |
25 | Then navigate to your Plugin and enable WordPress Comments Telegram Bot.
26 | ## 2. Create bot and find your user id
27 | Talk to [@BotFather](https://t.me/BotFather), create your own bot and copy bot token.
28 |
29 | Talk to [@get_id_bot](https://t.me/get_id_bot), get your own user id.
30 |
31 | ## 3. Edit configuration on WordPress
32 | Fill in your token, user id and proxy if needed.
33 | 
34 |
35 | ## 3. Download binary on GitHub Release
36 | [GitHub Release](https://github.com/BennyThink/wp-comments-tgbot/releases)
37 |
38 | ## 4. Create config
39 | Create your own `config.json`
40 | ```json
41 | {
42 | "username": "admin",
43 | "password": "admin",
44 | "url": "http://localhost/",
45 | "token": "907J6Tw",
46 | "uid": "23231321",
47 | "admin": 1,
48 | "tail": "本评论由Telegram Bot回复~❤️"
49 | }
50 | ```
51 | Explanations:
52 | * username & password: username and password for WordPress
53 | * url: site url, must include `/` as suffix.
54 | * token: Telegram bot token
55 | * uid: your Telegram user id
56 | * admin: your user id in WordPress, typically it should be 1
57 | * tail: suffix message to your reply.
58 |
59 | ## 5. Run
60 | Supply `config.json` as `-c /path/tp/config.json`. By default it will search on working directory.
61 | ```shell script
62 | chmod u+x /path/to/bot
63 | /path/to/bot -c /path/to/config.json
64 | ```
65 | ### cli arguments
66 | ```text
67 | -c file set configuration file (default "config.json")
68 | -f force to run even on http sites.
69 | -h this help
70 | -v show version and exit
71 | ```
72 | # Q&A
73 | ## 1. No email notifications while replying by telegram bot?
74 | Potentially bug about your theme/plugin. Try to find the code of sending email.
75 |
76 | You may find some hook like this:
77 | ``php
78 | add_action('comment_post', 'comment_mail_notify');
79 | ``
80 | Change `comment_post` to `wp_insert_comment` will solve this issue.
81 |
82 | [Reference commit](https://github.com/BennyThink/WordPressGit/commit/c64a3a5e70e10239ba9217debf16a075e2a13874)
83 |
84 | # Credits
85 | * [Basic Auth](https://github.com/WP-API/Basic-Auth)
86 |
87 | # License
88 | GPLv2
--------------------------------------------------------------------------------
/basic-auth.php:
--------------------------------------------------------------------------------
1 | ID;
42 | }
43 |
44 | add_filter( 'determine_current_user', 'json_basic_auth_handler', 20 );
45 |
46 | function json_basic_auth_error( $error ) {
47 | // Passthrough other errors
48 | if ( ! empty( $error ) ) {
49 | return $error;
50 | }
51 |
52 | global $wp_json_basic_auth_error;
53 |
54 | return $wp_json_basic_auth_error;
55 | }
56 |
57 | add_filter( 'rest_authentication_errors', 'json_basic_auth_error' );
58 |
--------------------------------------------------------------------------------
/bot.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "encoding/base64"
6 | "encoding/json"
7 | "flag"
8 | "fmt"
9 | tb "gopkg.in/tucnak/telebot.v2"
10 | "io/ioutil"
11 | "log"
12 | "math/rand"
13 | "net/http"
14 | "os"
15 | "strings"
16 | "time"
17 | )
18 | import "github.com/tgbot-collection/tgbot_ping"
19 |
20 | const version = "0.0.1"
21 |
22 | var (
23 | h, v, f bool
24 | c string
25 | )
26 |
27 | type config struct {
28 | Username string `json:"username"`
29 | Password string `json:"password"`
30 | Url string `json:"url"`
31 | Token string `json:"token"`
32 | Uid int64 `json:"uid,string"`
33 | Admin int `json:"admin"`
34 | Tail string `json:"tail,omitempty"`
35 | }
36 |
37 | func init() {
38 | flag.BoolVar(&h, "h", false, "this help")
39 | flag.BoolVar(&v, "v", false, "show version and exit")
40 | flag.BoolVar(&f, "f", false, "force to run even on http sites.")
41 | flag.StringVar(&c, "c", "config.json", "set configuration `file`")
42 |
43 | }
44 | func getArgs() config {
45 | flag.Parse()
46 |
47 | if v {
48 | fmt.Println(version)
49 | os.Exit(0)
50 | }
51 | if h {
52 | flag.Usage()
53 | os.Exit(0)
54 | }
55 | var configFile = c
56 | configData, err := readConfig(configFile)
57 |
58 | if err != nil {
59 | fmt.Printf("config file is corrupted or not found. \n%v\n", err)
60 | flag.Usage()
61 | os.Exit(2)
62 |
63 | } else if !f && !strings.HasPrefix(configData.Url, "https://") {
64 | fmt.Println("Your website is not https. Exit now.")
65 | fmt.Println("Please use -f to force start.")
66 | os.Exit(1)
67 | } else {
68 | fmt.Println("Okay let's roll😄")
69 | }
70 | return configData
71 |
72 | }
73 |
74 | func readConfig(cp string) (config, error) {
75 |
76 | jsonFile, err := os.Open(cp)
77 | if err != nil {
78 | return config{}, err
79 | }
80 | defer jsonFile.Close()
81 |
82 | var conf config
83 | byteValue, _ := ioutil.ReadAll(jsonFile)
84 | err = json.Unmarshal(byteValue, &conf)
85 | return conf, err
86 | }
87 |
88 | func randomEmoji() string {
89 |
90 | reasons := []string{"🙃", "🤨", "🤪", "😒", "🙂", "🥶", "🤔", "😶", "😐"}
91 | rand.Seed(time.Now().Unix())
92 | return reasons[rand.Intn(len(reasons))]
93 | }
94 |
95 | func replyComment(msg, reply string, conf config) (result string) {
96 | type postFormat struct {
97 | Author int `json:"author"`
98 | AuthorUserAgent string `json:"author_user_agent"`
99 | Content string `json:"content"`
100 | Parent string `json:"parent"`
101 | Post string `json:"post"`
102 | }
103 | reply += conf.Tail
104 | g := strings.Split(strings.Split(msg, "id: ")[1], ",")
105 | pid, cid := g[0], g[1]
106 | post := postFormat{
107 | Author: 1,
108 | AuthorUserAgent: "Telegram Bot by BennyThink",
109 | Content: reply,
110 | Parent: pid,
111 | Post: cid,
112 | }
113 | bytesData, err := json.Marshal(post)
114 | if err != nil {
115 | fmt.Println(err.Error())
116 | }
117 |
118 | data := bytes.NewReader(bytesData)
119 | wpApi := conf.Url + "wp-json/wp/v2/comments"
120 | request, err := http.NewRequest("POST", wpApi, data)
121 | auth := []byte(fmt.Sprintf("%s:%s", conf.Username, conf.Password))
122 | request.Header.Set("Content-Type", "application/json;charset=UTF-8")
123 | request.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString(auth))
124 | client := http.Client{}
125 | resp, err := client.Do(request)
126 | if err != nil {
127 | fmt.Println(err.Error())
128 | }
129 | var body struct {
130 | Message string `json:"message"`
131 | }
132 | if resp.StatusCode == 201 {
133 | result = "ok"
134 | } else {
135 | json.NewDecoder(resp.Body).Decode(&body)
136 | result = body.Message
137 | }
138 | return
139 |
140 | }
141 |
142 | func bot(conf config) {
143 | var owner = &tb.Chat{ID: conf.Uid}
144 | b, err := tb.NewBot(tb.Settings{
145 | Token: conf.Token,
146 | Poller: &tb.LongPoller{Timeout: 10 * time.Second},
147 | })
148 |
149 | if err != nil {
150 | log.Fatal(err)
151 | return
152 | }
153 | b.Handle(tb.OnText, func(m *tb.Message) {
154 | if m.Chat.ID != conf.Uid {
155 | b.Notify(m.Sender, "Typing")
156 | b.Send(m.Sender, randomEmoji())
157 | b.Send(m.Sender, "拒绝调戏。有问题请联系@BennyThink")
158 |
159 | } else if m.ReplyTo == nil {
160 | b.Notify(m.Sender, "Typing")
161 | b.Send(m.Sender, randomEmoji())
162 | } else {
163 | comment := m.ReplyTo.Text
164 | reply := m.Text
165 | b.Notify(m.Sender, "upload_document")
166 | resp := replyComment(comment, reply, conf)
167 | b.Notify(m.Sender, "typing")
168 | b.Send(owner, resp)
169 |
170 | }
171 | })
172 | b.Handle("/ping", func(m *tb.Message) {
173 | _ = b.Notify(m.Chat, tb.Typing)
174 | info := tgbot_ping.GetRuntime("botsrunner_wp-comments_1", "WordPress Comments Bot", "html")
175 | _, _ = b.Send(m.Chat, info, &tb.SendOptions{ParseMode: tb.ModeHTML})
176 | })
177 |
178 | b.Start()
179 | }
180 | func main() {
181 | c := getArgs()
182 | bot(c)
183 | }
184 |
--------------------------------------------------------------------------------
/bot.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=A Telegram Bot for WordPress comments.
3 | After=network.target network-online.target nss-lookup.target
4 |
5 | [Service]
6 | User=nobody
7 |
8 | Restart=on-failure
9 | Type=simple
10 | WorkingDirectory=/opt/bin
11 | ExecStart=/opt/bin/bot
12 |
13 | [Install]
14 | WantedBy=multi-user.target
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "username": "admin",
3 | "password": "admin",
4 | "url": "http://localhost/",
5 | "token": "907J6Tw",
6 | "uid": "23231321",
7 | "admin": 1,
8 | "tail": " --本评论由Telegram Bot回复~❤️"
9 | }
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module tgbot
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/tgbot-collection/tgbot_ping v1.0.2
7 | gopkg.in/tucnak/telebot.v2 v2.0.0-20200120165535-b6c3367fed99
8 | )
9 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48 h1:/EMHruHCFXR9xClkGV/t0rmHrdhX4+trQUcBqjwc9xE=
2 | code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48/go.mod h1:wN/zk7mhREp/oviagqUXY3EwuHhWyOvAdsn5Y4CzOrc=
3 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
6 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
7 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
8 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
9 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
10 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
11 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
12 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
13 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
14 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
15 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
16 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
17 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
18 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
19 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
20 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
21 | github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
22 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
23 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
24 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
25 | github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
26 | github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
27 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
28 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
29 | github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
30 | github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
31 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
32 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
33 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
34 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
35 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
36 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
37 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
38 | github.com/tgbot-collection/tgbot_ping v1.0.2 h1:CGKk6HCe3y87BQYHMKUpds3oOtSq0YcHUuezp3i0WZ0=
39 | github.com/tgbot-collection/tgbot_ping v1.0.2/go.mod h1:ee78nvZdBml2vl0yIKDGiNt/g8KFUHjB3s8gCevgrFc=
40 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
41 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
42 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
43 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
44 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
45 | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M=
46 | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
47 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
48 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
49 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
50 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
51 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
52 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
53 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
54 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
55 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
56 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
57 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
58 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
59 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
60 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
61 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
62 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
63 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
64 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
65 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
66 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
67 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
68 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
69 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
70 | google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
71 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
72 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
73 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
74 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
75 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
76 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
77 | gopkg.in/tucnak/telebot.v2 v2.0.0-20200120165535-b6c3367fed99 h1:EMQ2hWPVLRFE/1jEruLFkhy1ACshSEg3X3PeCtGM8mA=
78 | gopkg.in/tucnak/telebot.v2 v2.0.0-20200120165535-b6c3367fed99/go.mod h1:+//wyPtHTeW2kfyEBwB05Hqnxev7AGrsLIyylSH++KU=
79 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
80 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
81 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
82 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
83 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
84 |
--------------------------------------------------------------------------------
/pics/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tgbot-collection/wp-comments/133421305ebfc26546a43611ce498273902641c8/pics/1.png
--------------------------------------------------------------------------------
/pics/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tgbot-collection/wp-comments/133421305ebfc26546a43611ce498273902641c8/pics/2.jpg
--------------------------------------------------------------------------------
/pics/plugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tgbot-collection/wp-comments/133421305ebfc26546a43611ce498273902641c8/pics/plugin.png
--------------------------------------------------------------------------------
/test.php:
--------------------------------------------------------------------------------
1 | %s', $url, $text);
71 | }
72 |
73 | $token = $options["tg_token"];
74 | $uid = $options["tg_uid"];
75 | $proxy = $options["tg_proxy"];
76 | $api = "https://api.telegram.org/bot$token/sendMessage";
77 |
78 | $text = sprintf("A new comment on %s
79 |
80 | Title: %s
81 | User: %s (IP: %s %s)
82 | Email: %s
83 | Website: %s
84 | Comment:
85 | %s
86 |
87 | See all comments here:
88 | %s
89 |
90 | Permanent url: %s
91 |
92 | id: %s,%s
93 | ",
94 | a_link($comment_data["comment_website"], get_bloginfo()),
95 | $comment_data["comment_post_title"],
96 | $comment_data["comment_author"],
97 | $comment_data["comment_author_IP"], convertIP2Location($comment_data["comment_author_IP"]),
98 | $comment_data["comment_author_email"],
99 | a_link($comment_data["comment_author_url"]),
100 | $comment_data["comment_content"],
101 | a_link($comment_data['post_link'] . '#comments'),
102 | a_link($comment_data['comment_permlink']),
103 | $comment_data["comment_ID"], $comment_data["comment_post_ID"]
104 | );
105 | $postdata = http_build_query(array('chat_id' => $uid, 'parse_mode' => "html", "text" => $text));
106 | $opts = array(
107 | 'http' => array(
108 | 'proxy' => 'tcp://127.0.0.1:1087',
109 | 'method' => 'POST',
110 | 'header' => 'Content-type: application/x-www-form-urlencoded',
111 | 'content' => $postdata
112 | )
113 | );
114 | if ($proxy) {
115 | $opts["http"]["proxy"] = "tcp://$proxy";
116 | } else {
117 | unset($opts["http"]["proxy"]);
118 | }
119 |
120 | $context = stream_context_create($opts);
121 |
122 | file_get_contents($api, false, $context);
123 |
124 | }
125 |
126 | add_filter('plugin_action_links', 'bt_tgbot_settings', 10, 2);
127 | function bt_tgbot_settings($links, $file)
128 | {
129 | static $this_plugin;
130 | if (!$this_plugin) {
131 | $this_plugin = plugin_basename(__FILE__);
132 | }
133 |
134 | if ($file == $this_plugin) {
135 | $settings_link = 'Settings';
136 | array_unshift($links, $settings_link);
137 | }
138 |
139 | return $links;
140 | }
141 |
142 |
143 | add_action('admin_menu', 'bt_tgbot_admin_add_page');
144 | function bt_tgbot_admin_add_page()
145 | {
146 | add_options_page(
147 | 'wp tgbot 设置页面',
148 | 'wptgbot 设置',
149 | 'manage_options',
150 | 'wptgbot',
151 | 'bt_tgbot_options_page');
152 | }
153 |
154 | function bt_tgbot_options_page()
155 | {
156 | ?>
157 |