09月13, 2022

Pingback 功能

WordPress有一个功能:当有人引用本站文章时,会添加一条评论在文后,显示引用页面的信息;发博客时WordPress也会通知文章内每个链接,让它知道自己被引用了。这个功能非常赞,让博客间的互动变得更容易。看到屈屈文章Pingback和Xml-RPC便心血来潮,于是在自己博客中添加了此功能。

Pingback是基于XML-RPC协议,需要实现一个client和一个serverclient用于文章更新时ping对方服务;server用于处理对方的ping。

具体实现

client

  1. 文章更新时候,提取文章内引用链接。

  2. 请求引用链接,获取server地址(响应头:X-Pingback)。

const Base = require('./base');
const fetch = require('node-fetch');
const https = require('https');
// 忽略HTTPS证书验证
const agentOptions = {
    agent: new https.Agent({ rejectUnauthorized: false }),
};
const isDev = think.env === 'development';
module.exports = class extends Base {
    async pingAction() {
        const options = await this.model('options').getOptions();
        const pingback = JSON.parse(options.pingback);
        const data = this.post();
        const params = [];
        //1.提取url链接
        // eslint--next-line no-useless-escape
        const urls = data.markdown_content.match(/[^\!]{0,1}\[.*\]\((.*?)\)/gms).map((url) => {
            return url.replace(/.*\[.*\]\((.*)\)/, '$1');
        });
        //2.获取url对应pingback server
        for (let i = 0; i < urls.length; i++) {
            if (urls[i].endsWith('?pingback=true')) {
                continue;
            }
            const pingbackServer = await fetch(urls[i], agentOptions).then((res) => {
                return res.headers.get('X-Pingback') || '';
            });
            if (pingbackServer !== '') {
                params.push({
                    pagelinkedfrom: `${this.ctx.request.origin}/post/${data.pathname}.html`,
                    pagelinkedto: urls[i],
                    pingbackServer: pingbackServer,
                });
            }
        }
        //3.通过本地pingback client 调用 远程pingback server
        let isPingback = false;
        for (let i = 0; i < params.length; i++) {
            const res = await fetch(isDev ? 'http://127.0.0.1:8361' : pingback.client, {
                method: 'POST',
                body: JSON.stringify(params[i]),
            }).then((res) => {
                if (res.status === 200) {
                    return 'success';
                }
                return 'fail';
            });
            if (res === 'success') {
                data.markdown_content = data.markdown_content.replace(
                    params[i].pagelinkedto,
                    params[i].pagelinkedto + '?pingback=true',
                );
                isPingback = true;
            }
        }
        if (isPingback) {
            return this.success(data.markdown_content);
        }
        return this.fail('pingback exist or error');
    }

    // async backAction(){
    //     return this.success()
    // }
};
  1. 发送本文地址、引用地址到server。
from http.server import HTTPServer, SimpleHTTPRequestHandler
import xmlrpc.client
import json
host = ('localhost', 8361)

class Resquest(SimpleHTTPRequestHandler):
    
    def do_POST(self):
        post = self.rfile.read(int(self.headers['content-length']))   
        post = json.loads(post.decode("utf-8", 'ignore'))
        pingbackServer = post['pingbackServer']
        try:
            with xmlrpc.client.ServerProxy(pingbackServer) as proxy:
                res = proxy.pingback(post["pagelinkedfrom"],post["pagelinkedto"])
                if res == "success":
                    self.send_response(200)
                    self.send_header("Content-type","text/plain")    
                    self.end_headers()
                    self.wfile.write(res.encode()) 
                else:
                    self.send_error(code=500,message="fail")
        except Exception:
            print(Exception)
            self.send_error(code=500,message="fail")


 
if __name__ == '__main__':
    server = HTTPServer(host, Resquest)
    print("pingback client, listen at: %s:%s" % host)
    server.serve_forever()

server

  1. 提取上述本文地址的标题。

  2. 调用评论系统api。


import xmlrpc.server
import requests
import re

blogCommentServer = 'https://comment.imyoyo.xyz/comment'
host = ('localhost', 8362)
def extractTitle(url):
    r = requests.get(url, verify=False).text
    return re.findall(r"<title>(.*)</title>", r)[0]

def pingback(pagelinkedfrom , pagelinkedto):
    #1.提取pagelinkedfrom标题
    title = extractTitle(pagelinkedfrom)
    #2.添加评论
    data={
        "comment":"博客引文 :[{}]({})".format(title,pagelinkedfrom),
        "nick":"引用助手",
        "mail":"huyong@bupt.edu.cn",
        "link":"https://imyoyo.xyz/",
        "url":pagelinkedto[pagelinkedto.find("/post"):]

    }
    headers={
        "content-type": "application/json",
        "authorization": "xxxxxx"
    }
    r = requests.post(blogCommentServer,headers=headers,json=data,verify=False).json()
    print(r)
    if r["errno"] == 0:
        return "success"
    else:
        return "fail"

def main():
    with xmlrpc.server.SimpleXMLRPCServer(host) as server:
        server.register_introspection_functions()
        server.register_function(pingback)
        server.serve_forever()

if __name__ == "__main__":
    print("pingback server, listen at: %s:%s" % host)
    main()

效果

遇到问题

  • python requests 在python3.7环境运行报错。

  • Nginx域名转发配置报错。

参考文献

本文链接:https://imyoyo.xyz/post/pingback.html

-- EOF --

Comments