标签 typecho 下的文章

typecho文章自动发布到telegram频道

最近开设了willnet.net的电报频道,想发布新文章后自动发布到电报频道
搜索了一圈没有发现能实现typecho新文章推送到电报频道的插件,只有一个comments2telegram可以发送新评论到电报,wordpress倒是有个wptelegram可以实现,但是一直担心wordpress的安全问题没有切换到wp。

没有办法,自己组装个轮子吧。github搜了一圈发现一个telegram.sh,简单高效,我只要用shell拿到最新文章的url就可以组装起来了。

首先要新建一个bot
telegram搜索botfather
开始后输入bot的名字和用户名会得到类似11234567890:fdhslfdskagjdgjdka的token,记下来填入下面的脚本中

然后获取你的频道id
telegram搜索getidsbot
转发频道的一条消息到这个bot就会得到频道id,类似-1001234567890,记下来填入下面脚本中

最后添加bot到你的频道,设定个定时任务每分钟执行脚本,判断是不是最新文章,是的话推送到频道。

下面是代码,没有几行,就贴这吧
telegram.sh

#!/bin/bash

VERSION="0.2"
TOKEN=""
CHATS=()
DEBUG=false
DRY_RUN=false

IMAGE_FILE=""
DOCUMENT_FILE=""
PARSE_MODE=""
CODE_MODE=0
CRON_MODE=0
ACTION=""
DISABLE_WEB_PAGE_PREVIEW=false
DISABLE_NOTIFICATION=false

URL="https://api.telegram.org/bot"
FILE_URL="https://api.telegram.org/file/bot"
CURL_OPTIONS="-s"

TELEGRAM_TOKEN="你的bot token"
TELEGRAM_CHAT="你的频道id"

HAS_JQ=false
hash jq >/dev/null 2>&1 && HAS_JQ=true
      

function help {
    version
    echo "Usage: $0 [options] [message]"
    echo
    echo "OPTIONS are:"
    echo "    -t <TOKEN>       Telegram bot token to use. See ENVIRONMENT for more information."
    echo "    -c <CHAT_ID>     Chat to use as recipient. Can be given more than once. See ENVIRONMENT for more information."
    echo "    -f <FILE>        Sends file."
    echo "    -i <FILE>        Sends file as image. This will fail if the file isn't an actual image file."
    echo "    -M               Enables Markdown processing at telegram."
    echo "    -H               Enables HTML processing at telegram."
    echo "    -C               Sends text as monospace code. Useful when piping command outputs into this tool."
    echo "    -r               Like -C, but if the first line starts with '+ ', it is specially formatted."
    echo "    -l               Fetch known chat_ids."
    echo "    -R               Receive a file sent via telegram."
    echo "    -D               Sets disable_web_page_preview parameter to, well, disable the preview for links to webpages."
    echo "                     Can also be set in config as TELEGRAM_DISABLE_WEB_PAGE_PREVIEW=true (see ENVIRONMENT)"
    echo "                     This feature is only supported for text messages, not when sending files or images (-f, -i)."
    echo "    -N               Diables notifications on clients. Users will receive a notification with no sound."
    echo "                     Can also be set in config as TELEGRAM_DISABLE_NOTIFICATION=true (see ENVIRONMENT)"
    echo
    echo "DEBUGGING OPTIONS are:"
    echo "    -v               Display lots of more or less useful information."
    echo "    -j               Pretend you don't have JQ installed."
    echo "    -n               Dry-run - don't send messages, only print them on screen."
    echo
    echo "Message can be '-', in that case STDIN will be used."
    echo
    echo "ENVIRONMENT"
    echo "    TOKEN and CHAT_ID are required. You can set them in four different ways:"
    echo "      1) globally in /etc/telegram.sh.conf"
    echo "      2) user-local in ~/.telegram.sh"
    echo "      3) via environment variables TELEGRAM_TOKEN and TELEGRAM_CHAT"
    echo "      4) via options -t and -c"
    echo "    Later methods overwrite earlier settings, so you can easily override global settings."
    echo "    Please be aware that you shuld keep your telegram token secret!" 
    echo
    exit
}

function version {
    echo "telegram.sh version $VERSION"
    echo "by Fabian Schlenz"
}

function list_chats {
    log "$URL$TOKEN"
    response=`curl $CURL_OPTIONS $URL$TOKEN/getUpdates`
    log "$response"
    
    if [ "$HAS_JQ" = true ]; then
        echo "These are the available chats that I can find right now. The ID is the number at the front."
        echo "If there are no chats or the chat you are looking for isn't there, run this command again"
        echo "after sending a message to your bot via telegram."
        echo
        jq -r '.result | .[].message.chat | "\(.id|tostring) - \(.first_name) \(.last_name) (@\(.username))"' 2>/dev/null <<< "$response" || {
            echo "Could not parse reponse from Telegram."
            echo "Response was: $response"
            exit 1
        }
    else
        echo "You don't have jq installed. I'm afraid I can't parse the JSON from telegram without it."
        echo "So I'll have you do it. ;-)"
        echo
        echo "Please look for your chat_id in this output by yourself."
        echo 'Look for something like "chat":{"id":<CHAT_ID> and verify that first_name, last_name and'
        echo "username match your expected chat."
        echo
        echo "If there are no chats listed or the chat you are looking for isn't there, try again after"
        echo "sending a message to your bot via telegram."
        echo
        echo $response
    fi
}

function receive_file {
    if [ "$HAS_JQ" = false ]; then
        echo "You need to have jq installed in order to be able to download files."
        exit 1
    fi
    
    result=`curl $CURL_OPTIONS $URL$TOKEN/getUpdates?allowed_updates=message`
    log "$result"
    # {"ok":true,"result":[
    #   {
    #     "update_id":441727866,
    #     "message":{
    #       "message_id":8339,
    #       "from":{"id":15773,"is_bot":false,"first_name":"Fabian","last_name":"Schlenz","username":"fabianonline","language_code":"de"},
    #       "chat":{"id":15773,"first_name":"Fabian","last_name":"Schlenz","username":"fabianonline","type":"private"},
    #       "date":1526564127,
    #       "document":{"file_name":"desktop.ini","file_id":"BQAav-HkXugI","file_size":282}}}]}
    file_id=`jq -r '.result[-1].message.document.file_id' <<< "$result"`
    log "file_id: $file_id"
    if [ "$file_id" == "null" ]; then
        echo "Last message received apparently didn't contain a file. Aborting."
        exit 1
    fi
    file_name=`jq -r '.result[-1].message.document.file_name' <<< "$result"`
    log "file_name: $file_name"
    result=`curl $CURL_OPTIONS $URL$TOKEN/getFile?file_id=$file_id`
    log $result
    # {"ok":true,"result":{"file_id":"BQAav-HkXugI","file_size":282,"file_path":"documents/file_271.ini"}}
    path=`jq -r '.result.file_path' <<< "$result"`
    log "path: $path"
    if [ "$path" == "null" ]; then
        echo "Could not parse telegram's response to getFile. Aborting."
        exit 1
    fi
    file_name="`date +%s`_$file_name"
    log "file_name: $file_name"
    if [ -e "$file_name" ]; then
        echo "File $file_name already exists. This is unexpected, so I'm quitting now."
        exit 1
    fi
    curl $FILE_URL$TOKEN/$path --output "$file_name"
    echo "File downloaded as $file_name"
}

function log {
    [ "$DEBUG" = true ] && echo "DEBUG: $1"
}

function check_file {
    if [ ! -e "$1" ]; then
        echo "The file $1 does not exist."
        exit 1
    fi
    
    size=$(stat -c%s "$1")
    if (( size > 52428800 )); then
        echo "File $1 is breaking the file size limit imposed on Telegram bots (currently 50MB)."
        exit 1
    fi
}

function escapeMarkdown {
    res="${1//\*/∗}"
    res="${res//_/_}"
    res="${res//\`/‵}"
    #res="${res//\//∕}"
    echo "$res"
}

while getopts "t:c:i:f:MHCrhlvjnRDN" opt; do
    case $opt in
        t)
            TOKEN="$OPTARG"
            ;;
        c)
            CHATS+=("$OPTARG")
            ;;
        i)
            IMAGE_FILE="$OPTARG"
            ;;
        f)
            DOCUMENT_FILE="$OPTARG"
            ;;
        M)
            PARSE_MODE="Markdown"
            ;;
        H)
            PARSE_MODE="HTML"
            ;;
        C)
            PARSE_MODE="Markdown"
            CODE_MODE=1
            ;;
        r)
            PARSE_MODE="Markdown"
            CRON_MODE=1
            ;;
        l)
            ACTION="list_chats"
            ;;
        v)
            DEBUG=true
            ;;
        j)
            HAS_JQ=false
            ;;
        n)
            DRY_RUN=true
            ;;
        R)
            ACTION="receive_file"
            ;;
        D)
            DISABLE_WEB_PAGE_PREVIEW=true
            ;;
        N)
            DISABLE_NOTIFICATION=true
            ;;
        ?|h)
            help
            ;;
        :)
            echo "Option -$OPTARG needs an argument."
            exit 1
            ;;
        \?)
            echo "Invalid option -$OPTARG"
            exit 1
            ;;
    esac
done

if [ "$CRON_MODE" -eq 1 ] && [ "$CODE_MODE" -eq 1 ]; then
    echo "You can either use -C or -r, but not both."
    exit 1
fi

log "TOKEN is now $TOKEN"
log "CHATS is now ${CHATS[*]}"

[ -z "$TOKEN" ] && TOKEN=$TELEGRAM_TOKEN
[ ${#CHATS[@]} -eq 0 ] && CHATS=($TELEGRAM_CHAT)

log "TOKEN is now $TOKEN"
log "CHATS is now ${CHATS[*]}"

log "Importing config file(s)..."

[ -r /etc/telegram.sh.conf ] && source /etc/telegram.sh.conf
[ -r ~/.telegram.sh ] && source ~/.telegram.sh

[ -z "$TOKEN" ] && TOKEN=$TELEGRAM_TOKEN
[ ${#CHATS[@]} -eq 0 ] && CHATS=($TELEGRAM_CHAT)
[ -n "$TELEGRAM_DISABLE_WEB_PAGE_PREVIEW" ] && DISABLE_WEB_PAGE_PREVIEW="$TELEGRAM_DISABLE_WEB_PAGE_PREVIEW"
[ -n "$TELEGRAM_DISABLE_NOTIFICATION" ] && DISABLE_NOTIFICATION="$TELEGRAM_DISABLE_NOTIFICATION"

log "TOKEN is now $TOKEN"
log "CHATS is now ${CHATS[*]}"
log "DISABLE_WEB_PAGE_PREVIEW is now $DISABLE_WEB_PAGE_PREVIEW"
log "DISABLE_NOTIFICATION is now $DISABLE_NOTIFICATION"

if [ -z "$TOKEN" ]; then
    echo "No bot token was given."
    exit 1
fi

if [ ${#CHATS[@]} -eq 0 ] && [ -z "$ACTION" ]; then
    echo "No chat(s) given."
    exit 1
fi

if [ "$ACTION" = "list_chats" ]; then
    list_chats
    exit 0
fi

if [ "$ACTION" = "receive_file" ]; then
    receive_file
    exit 0
fi

shift $((OPTIND - 1))
TEXT="$1"
log "Text: $TEXT"

[ "$TEXT" = "-" ] && TEXT=$(</dev/stdin)

log "Text: $TEXT"

if [ $CODE_MODE -eq 1 ]; then
    TEXT='```'$'\n'$TEXT$'\n''```'
fi

if [ $CRON_MODE -eq 1 ]; then
    ONLY_COMMANDS=1
    while read line; do
        if [ "${line:0:2}" = "+ " ]; then
            ONLY_COMMANDS=0
        fi
    done <<< "$TEXT"
    
    if [ "$ONLY_COMMANDS" -eq 1 ]; then
        exit 0
    fi
    
    TEXT='```'$'\n'$TEXT$'\n''```'
    
    #FIRST_LINE=1
    #BLOCK_OPEN=0
    #NEW_TEXT=""
    #while read line; do
    #    if [ "${line:0:2}" = "+ " ]; then
    #        if [ "$BLOCK_OPEN" -eq 1 ]; then
    #            NEW_TEXT="$NEW_TEXT"'```'$'\n'
    #            BLOCK_OPEN=0
    #        fi
    #        NEW_TEXT="$NEW_TEXT*`escapeMarkdown "${line:2}"`*"$'\n'
    #    else
    #        if [ "$BLOCK_OPEN" -eq 0 ]; then
    #            NEW_TEXT="$NEW_TEXT"'```'$'\n'
    #            BLOCK_OPEN=1
    #        fi
    #        NEW_TEXT="$NEW_TEXT$line"$'\n'
    #    fi
    #done <<< "$TEXT"
    #[ "$BLOCK_OPEN" -eq 1 ] && NEW_TEXT="$NEW_TEXT"'```'
    #TEXT="$NEW_TEXT"
fi

log "Text: $TEXT"

if [ -z "$TEXT" ] && [ -z "$DOCUMENT_FILE" ] && [ -z "$IMAGE_FILE" ]; then
    echo "Neither text nor image or other file given."
    exit 1
fi

if [ -n "$DOCUMENT_FILE" ] && [ -n "$IMAGE_FILE" ]; then
    echo "You can't send a file AND an image at the same time."
    exit 1
fi

if [ -n "$DOCUMENT_FILE" ]; then
    check_file "$DOCUMENT_FILE"
    
    CURL_OPTIONS="$CURL_OPTIONS --form document=@$DOCUMENT_FILE"
    CURL_OPTIONS="$CURL_OPTIONS --form caption=<-"
    METHOD="sendDocument"
elif [ -n "$IMAGE_FILE" ]; then
    check_file "$IMAGE_FILE"
    CURL_OPTIONS="$CURL_OPTIONS --form photo=@$IMAGE_FILE"
    CURL_OPTIONS="$CURL_OPTIONS --form caption=<-"
    METHOD="sendPhoto"
else
    CURL_OPTIONS="$CURL_OPTIONS --form text=<-"
    [ -n "$PARSE_MODE" ] && CURL_OPTIONS="$CURL_OPTIONS --form-string parse_mode=$PARSE_MODE"
    [ "$DISABLE_WEB_PAGE_PREVIEW" = true ] && CURL_OPTIONS="$CURL_OPTIONS --form-string disable_web_page_preview=true"
    METHOD="sendMessage"
fi

[ "$DISABLE_NOTIFICATION" = true ] && CURL_OPTIONS="$CURL_OPTIONS --form-string disable_notification=true"

for id in "${CHATS[@]}"; do
    MY_CURL_OPTIONS="$CURL_OPTIONS --form-string chat_id=$id $URL$TOKEN/$METHOD"
    if [ "$DRY_RUN" = true ]; then
        echo "Executing: curl $MY_CURL_OPTIONS"
        echo "     Text: $TEXT"
        echo
        status=0
        response='{"ok": true}'
    else
        response=`curl $MY_CURL_OPTIONS <<< "$TEXT"`
        status=$?
    fi
    log "Response was: $response"
    if [ $status -ne 0 ]; then
        echo "curl reported an error. Exit code was: $status."
        echo "Response was: $response"
        echo "Quitting."
        exit $status
    fi

    if [ "$HAS_JQ" = true ]; then
        if [ "`jq -r '.ok' <<< "$response"`" != "true" ]; then
            echo "Telegram reported following error:"
            jq -r '"\(.error_code): \(.description)"' <<< "$response"
            echo "Quitting."
            exit 1
        fi
    else
        if [[ "$response" != '{"ok":true'* ]]; then
            echo "Telegram reported an error:"
            echo $response
            echo "Quitting."
            exit 1
        fi
    fi
done

newarticle_bot.sh

LAST_ARTICLE_URL=`cat /root/telegram_bot/lastarticleurl`
NEW_ARTICLE_URL=`curl -s https://你的域名 |grep "<li><h3><a" | awk -F "[\"\"]" '{print $2}'`
if [ "$LAST_ARTICLE_URL"x = "$NEW_ARTICLE_URL"x ];then
echo "no new article!"
else
echo $NEW_ARTICLE_URL |/root/telegram_bot/telegram.sh -
echo $NEW_ARTICLE_URL >/root/telegram_bot/lastarticleurl
fi

crontab -l

* * * * * /root/telegram_bot/newarticle_bot.sh

typecho添加按文章修改时间排序的独立页面

搜索了一圈都没有发现现成的例子,动手自己改了一个。
总体方法参考了不安装插件添加归档页面的方法typecho添加接口方法

这个添加归档页面的方法中使用了typecho内核预制一个Widget接口,Widget_Contents_Post_Recent,位于typecho/var/Widget/Contents/Post中的Recent.php中。
仿照Recent.php在同目录下添加Update.php文件,内容如下

<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;

class Widget_Contents_Post_Update extends Widget_Abstract_Contents
{
    /**
     * 执行函数
     *
     * @access public
     * @return void
     */
    public function execute()
    {
        $this->parameter->setDefault(array('pageSize' => $this->options->postsListSize));

        $this->db->fetchAll($this->select()
        ->where('table.contents.status = ?', 'publish')
        ->where('table.contents.modified < ?', $this->options->time)
        ->where('table.contents.type = ?', 'post')
        ->order('table.contents.modified', Typecho_Db::SORT_DESC)
        ->limit($this->parameter->pageSize), array($this, 'push'));
    }
}

主要修改了接口名称Widget_Contents_Post_Update,数据库字段table.contents.modified

然后在主题目录typecho/usr/themes/xxx/下添加模版,page-update.php,内容如下

<?php    
   /**  
    * update    
    * @package custom   
    */    
$this->need('header.php');?>   
<div class="col-8" id="main">
    <div class="res-cons">
            <article class="post">
                <div class="post-content-pages">
                    <?php $this->widget('Widget_Contents_Post_Update', 'pageSize=30')->to($archives);   
            $year=0; $mon=0; $i=0; $j=0;   
            $output = '<div id="archives">';   
            while($archives->next()):   
            $year_tmp = date('Y',$archives->modified);   
             $mon_tmp = date('m',$archives->modified);   
             //var_dump($year_tmp);   
             $y=$year; $m=$mon;   
             if ($mon != $mon_tmp && $mon > 0) $output .= '</ul></li>';   
             if ($year != $year_tmp && $year > 0) $output .= '</ul>';   
             if ($year != $year_tmp) {   
                 $year = $year_tmp;   
                 $output .= '<div class="al_year">'. $year .' 年</div><ul class="al_mon_list">'; //输出年份   
             }   
             $output .= '<li>'.date('m/d',$archives->modified).'<a href="'.$archives->permalink .'">'. $archives->title .'</a></li>'; //输出文章日期和标题   
        endwhile;   
        $output .= '</ul></li></ul></div>';   
        echo $output;   
        ?>
                </div>
            </article>
    </div>
</div>
<?php $this->need('sidebar.php'); ?>
<?php $this->need('footer.php'); ?>

主要修改了模版名称update,接口名称Widget_Contents_Post_Update,变量名称modified

最后在typecho后台添加独立页面选择update这个模版。