This page looks best with JavaScript enabled

Mastodon | 记录大型魔改过程

 ·  ☕ 8 min read

一直很想让长毛象支持 Markdown语法,但我本人却因为菜得看不懂真代码而多次碰壁。这次使用了小森林站长的docker镜像,主题和特色请看这里

注:小森林站源代码按照AGPL3协议开源,所有资源都不依赖小森林站长的服务器资源。自行部署可参考本文实践,没有经过大型魔改的站点理论上讲都可以平滑迁移。

本文的劳动成果部分属于mashiro,本文仅作备忘使用,鞠躬。

备份配置文件

cp docker-compose.yml docker-compose.save && vim docker-compose.save

原配置如下:

version: '3'
services:

  db:
    restart: always
    image: postgres:12.5-alpine
    shm_size: 256mb
    networks:
      - internal_network
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
    volumes:
      - ./postgres:/var/lib/postgresql/data

  redis:
    restart: always
    image: redis:6.0-alpine
    networks:
      - internal_network
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
    volumes:
      - ./redis:/data

  es:
    restart: always
    image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2
    environment:
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - "cluster.name=es-mastodon"
      - "discovery.type=single-node"
      - "bootstrap.memory_lock=true"
    networks:
      - internal_network
    healthcheck:
      test: ["CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1"]
    volumes:
      - ./elasticsearch:/usr/share/elasticsearch/data
    ulimits:
      memlock:
        soft: -1
        hard: -1


  web:
    build: .
    image: pullopen/mastodon:v3.4.4
    restart: always
    env_file: .env.production
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
    networks:
      - external_network
      - internal_network
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:3000/health || exit 1"]
    ports:
      - "127.0.0.1:3000:3000"
    depends_on:
      - db
      - redis
      - es
    volumes:
      - ./public/system:/mastodon/public/system

  streaming:
    build: .
    image: pullopen/mastodon:v3.4.4
    restart: always
    env_file: .env.production
    command: node ./streaming
    networks:
      - external_network
      - internal_network
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1"]
    ports:
      - "127.0.0.1:4000:4000"
    depends_on:
      - db
      - redis
      
  sidekiq:
    build: .
    image: pullopen/mastodon:v3.4.4
    restart: always
    env_file: .env.production
    command: bundle exec sidekiq
    depends_on:
      - db
      - redis
    networks:
      - external_network
      - internal_network
    volumes:
      - ./public/system:/mastodon/public/system
## Uncomment to enable federation with tor instances along with adding the following ENV variables
## http_proxy=http://privoxy:8118
## ALLOW_ACCESS_TO_HIDDEN_SERVICE=true
#  tor:
#    image: sirboops/tor
#    networks:
#      - external_network
#      - internal_network
#
#  privoxy:
#    image: sirboops/privoxy
#    volumes:
#      - ./priv-config:/opt/config
#    networks:
#      - external_network
#      - internal_network

networks:
  external_network:
  internal_network:
    internal: true

由于偷懒没有自行制作镜像,这里我用了pullopen站长的镜像pullopen/mastodon:v3.4.4,感谢她。

cp .env.production .env.production.save && vim .env.production.save

原配置如下:

# Generated with mastodon:setup on 2021-06-12 10:34:39 UTC
  
LOCAL_DOMAIN=toot.tantalum.life
SECRET_KEY_BASE=
OTP_SECRET=
VAPID_PRIVATE_KEY=
VAPID_PUBLIC_KEY=
DB_HOST=db
DB_PORT=5432
DB_NAME=mastodon_production
DB_USER=mastodon
DB_PASS=
REDIS_HOST=redis
REDIS_PORT=6379
SMTP_SERVER=smtp.zoho.com
SMTP_PORT=587
SMTP_LOGIN=notifications@yourdomain.com
SMTP_PASSWORD=
SMTP_AUTH_METHOD=plain
SMTP_OPENSSL_VERIFY_MODE=none
SMTP_FROM_ADDRESS=Mastodon <notifications@yourdomain.com>
S3_ENABLED=true
S3_BUCKET=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
S3_REGION=nl-ams
S3_PROTOCOL=https
S3_HOSTNAME=s3.nl-ams.scw.cloud
S3_ENDPOINT=https://s3.nl-ams.scw.cloud/
S3_ALIAS_HOST=
GITHUB_REPOSITORY=pullopen/mastodon
AUTHORIZED_FETCH=true   #安全模式开启
ES_ENABLED=true
ES_HOST=es
ES_PORT=9200

备份数据库防止意外。

加入魔改配置

经小森林站长提醒,如果默认用的是自定义主题,迁移前先把站点主题换成mastodon默认的深色主题。

运行docker-compose down使站点下线,开始我们的魔改工作。

打开docker-compose.yml示例代码,复制末尾的translate块部分。回到服务器运行nano docker-compose.yml,将刚刚复制的内容粘贴到服务器的docker-compose.yml文件的对应位置。

  • webstreamingsidekiq三项的image改为mashirozx/mastodon:latest,同时也将三个build字段注释掉。
  • es项的image改为mashirozx/elasticsearch-cnplugin:7.16.1,保存。

如果你没有开启全文搜索,请参考这个教程开启全文搜索,直接把image改为mashirozx/elasticsearch-cnplugin:7.16.1即可。在本文结束后再运行docker-compose run --rm web bin/tootctl search deploy建立嘟文的搜索索引。

打开.env.production示例代码,第77行至末尾部分是小森林站长的改动。复制这部分内容,回到服务器运行nano .env.production,直接在后面粘贴就可。其中需要改动的的几个地方:

  • MAX_TOOT_CHARS=500是嘟文字数最大值,改为你喜欢的数字。
  • PLAUSIBLE_ANALYTICS_SITE_DOMAIN=hello.2heng.xin去掉注释并改为你的域名。
  • 最末尾的GITHUB_OAUTH2GITLAB_OAUTH2配置是Github和Gitlab登录方式,可以大胆注释掉。

现在可以使站点重新上线了:

docker-compose up -d

后续工作

之前的文章里提到,站点升级后可能会遇到刷嘟时提示网络错误500的报错。

db_migrate
管理页面: 数据库迁移提醒

此时需要进行数据库迁移:

docker-compose run --rm web rails db:migrate

另外,在两次升级时跨版本号太多,或是经过大型魔改后还需要运行tootctl cache clear命令,否则可能会出现与外站通信延迟高的问题:

docker-compose run --rm web bin/tootctl cache clear

重启长毛象载入改动,否则数据库迁移命令不会生效:

docker-compose down
docker-compose up -d

自定义CSS样式

自定义CSS:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/*鼠标选中底色*/
*::selection {
    color: #fff;
    background-color: #b85798;
}

/*mastodon长图补丁 v2.1 by Shioko*/
.image-loader {
    align-items: center;
}
.zoomable-image {
    display: flex;
    max-height: 100%;
    max-width: 98%;
    overflow: auto !important;
}

.zoomable-image img {
	max-height: 100%;
    max-width:100%;
}

/*variable width*/
div.column {
 -webkit-box-flex:1;
 -ms-flex-positive:1;
 flex-grow:1
}

/*hashtag style blue by slashine 071320*/
.mention.hashtag{
background-color: #93AEFD36;
padding: 0px 4px;
text-align: center;
text-decoration: none;
display: inline-block;
border-style: dashed;
border-color: #93AEFD;
border-width: 0.5px;
border-radius: 2px;
margin-top: 2px;
margin-bottom: 2px;
}

/*emoji enlarge written by bgme*/
.reply-indicator__content .emojione, 
.status__content .emojione {
    width: 30px !important;
    height: 30px !important;
}

.emoji-mart-category .emoji-mart-emoji:hover span {
    width: 45px !important;
    height: 45px !important;
}

.emoji-mart-category .emoji-mart-emoji:hover {
    margin: 0 -12px;
}

/* START mastodon emoji scaling by @eh5@eh5.me */
.account__header__content,
.reply-indicator__content,
.status__content:not(.status__content--collapsed) {
overflow: unset;
}
.account__header__content .emojione,
.reply-indicator__content .emojione,
.status__content:not(.status__content--collapsed) .emojione {
position: relative;
z-index: 10;
transform-origin: center;
/* Animation duration */
transition: 200ms ease-in-out;
}
.account__header__content .emojione:hover,
.reply-indicator__content .emojione:hover,
.status__content:not(.status__content--collapsed) .emojione:hover {
z-index: 11;
/* Scale up 2.3 times */
transform: scale(2.3);
/* shadows around image edges */
filter: drop-shadow(0 0 1px #282c37);
}
.directory__card .account__header__content .emojione:hover {
transform: unset;
}
/* END mastodon emoji scaling by @eh5@eh5.me */

/*cat ears written by dmonad & neb*/
.notification .status__avatar::before,
.notification .status__avatar::after {
  display: none !important;
}

.status__wrapper .status:first-child .status__avatar::before,
.status__wrapper .status:first-child .status__avatar::after,
.entry.h-entry .status__avatar div::before,
.entry.h-entry .status__avatar div::after,
.entry.entry-reblog .status__avatar div::before,
.entry.entry-reblog .status__avatar div::after {
  content: "";
  display: inline-block;
  border: 4px solid;
  box-sizing: border-box;
  width: 50%;
  height: 50%;
  background-color: inherit;
  border-color: inherit;
  position: absolute;
  z-index: 0;
}

.status__avatar::before,
.entry.h-entry .status__avatar div::before,
.entry.entry-reblog .status__avatar div::before {
  border-radius: 75% 0 75% 75%;
  transform: rotate(-37.6deg) skew(-30deg);
  right: 0;
}

.status__avatar::after,
.entry.h-entry .status__avatar div::after,
.entry.entry-reblog .status__avatar div::after {
  border-radius: 0 75% 75%;
  transform: rotate(37.6deg) skew(30deg);
  top: 0;
  left: 0;
}

.detailed-status__display-name {
  overflow: visible !important;
}

.detailed-status__display-avatar {
  position: relative;
}

.detailed-status__display-avatar::before,
.detailed-status__display-avatar::after {
  content: "";
  display: inline-block;
  border: 4px solid;
  box-sizing: border-box;
  width: 24px;
  height: 24px;
  background-color: inherit;
  border-color: inherit;
  position: absolute;
  z-index: 0;
}

.detailed-status__display-avatar::before {
  border-radius: 75% 0 75% 75%;
  transform: rotate(-37.6deg) skew(-30deg);
  right: 0px;
}

.detailed-status__display-avatar::after {
  border-radius: 0 75% 75%;
  transform: rotate(37.6deg) skew(30deg);
  top: 0;
}

.account__avatar {
  border-radius: 100%;
  z-index: 1;
}

.status__avatar:hover::before,
.detailed-status__display-avatar:hover::before,
.entry.h-entry .status__avatar div:hover::before,
.entry.entry-reblog .status__avatar div:hover::before {
  animation: earwiggleright 1s infinite;
}

.status__avatar:hover::after,
.detailed-status__display-avatar:hover::after,
.entry.h-entry .status__avatar div:hover::after,
.entry.entry-reblog .status__avatar div:hover::after {
  animation: earwiggleleft 1s infinite;
}

@keyframes earwiggleleft {
  from { transform: rotate(37.6deg) skew(30deg); }
  25% { transform: rotate(10deg) skew(30deg); }
  50% { transform: rotate(20deg) skew(30deg); }
  75% { transform: rotate(0deg) skew(30deg); }
  to { transform: rotate(37.6deg) skew(30deg); }
}

@keyframes earwiggleright {
  from { transform: rotate(-37.6deg) skew(-30deg); }
  30% { transform: rotate(-10deg) skew(-30deg); }
  55% { transform: rotate(-20deg) skew(-30deg); }
  75% { transform: rotate(-0deg) skew(-30deg); }
  to { transform: rotate(-37.6deg) skew(-30deg); }
}

SSL证书续签

参考:How to install Mastodon Social Network with Docker on Ubuntu 18.04 LTS

Let’s Encrypt 证书的有效期为90天。 之后需要再次更新。可以创建一个cron作业来自动为您完成。

创建一个cron作业:

nano /etc/cron.daily/letsencrypt-renew

将以下内容复制并粘贴到文件中。

#!/usr/bin/env bash
certbot renew
systemctl reload nginx

使脚本可执行并重新启动cron守护程序,以便脚本每天运行:

chmod +x /etc/cron.daily/letsencrypt-renew
systemctl restart cron

从他站爬取自定义表情包

2022/04/22更新:
爬取自定义表情包

一般情况下,作为站长当发现其他站点有好看的表情包想拥有的时候,最简单粗暴的方式是去网页端的管理后台将他站表情包复制到自家站点的数据库中。但是这样需要一个一个勾选表情包并复制,非常低效,只适用于一次性复制少量表情包。那么如何一次性将他站的一整个表情分类(甚至全部分类)全都偷过来呢?

偷表情神器应运而生啦!下面介绍emojidownloader的使用方法。此工具由兔酱编写,感谢ta!

以2022年3月更新的emojidownloader v0.1.0版本为例:

打开项目最新版本的Releases页面,找到emoji_downloader_linux_x86_64,右击复制其网址。

回到服务器进入/opt/mastodon目录,运行:

wget 你复制的网址
chmod +x ./emoji_downloader_linux_x86_64
./emoji_downloader_linux_x86_64

就可以开始偷表情了!

选择从url下载mastodon emoji,输入想要爬取表情包的站点域名。

是否使用代理?(y/N)  #直接回车
是否设置Mastodon Cookie?(y/N)  #直接回车

当出现以下错误时,说明站点开启了authorized_fetch(安全模式),需要拥有对方站帐号才能继续爬取表情。如果顺利解析到表情分类,可以直接将本文划到最末尾。

2022-04-22 02:01:06.105254 prompt.go:84 <plParseJson> |MAN| [INFO] 解析中......
2022-04-22 02:01:06.557876 prompt.go:87 <plParseJson> |MAN| [ERROR] 解析失败!请检查 http code not correct! got 401,resp text is {"error":"This method requires an authenticated user"}
出现错误,按任意键结束

如果你拥有对方站的帐号,请在浏览器中登录。随后打开浏览器的网页开发者工具(以Firefox为例:右上角Burger Menus -> 更多工具 -> 网页开发者工具)

刷新页面,找到最后一个GET方法的条目,点开就可以在右侧看到cookie了:

将得到的cookie复制下来备用。回到服务器重新运行偷表情神器:

./emoji_downloader_linux_x86_64

来到是否设置Mastodon Cookie?(y/N)这一步,这次我们选择y,然后输入刚刚得到的cookie的值(_mastodon_session开头),稍等一下就可以成功解析出表情包了。根据脚本提示选择表情包分类,随后:

是否开启白名单emoji名称?(y/N)
是否替换emoji名称?(y/N)
请输入保存文件夹(默认:./myEmojis):
是否忽略下载中的单个emoji下载错误?(Y/n)
是否打包为压缩文件?(Y/n)
是否在打包为压缩文件后删除原始的下载文件(Y/n)
并发下载量(默认:16):
是否显示下载日志?(Y/n)

以上全部一路回车。脚本运行的最后会有导入表情包的提示:

非docker:
RAILS_ENV=production bin/tootctl emoji import --category=自定义表情分类名 表情tar.gz文件地址

docker:
docker cp ./表情地址 web服务docker名:/tmp/表情名.tar.gz
docker-compose exec web bin/tootctl emoji import --category=自定义表情分类名 /tmp/表情名.tar.gz

表情包的默认下载地址是./myEmojisweb服务docker名可以运行docker ps查看,一般是mastodon-web-1,表情分类命名随意设置。

最后一步的过程比较慢,需要把表情包写入数据库中。成功之后就可以使用新鲜出炉的表情包啦~


Roelxy
WRITTEN BY
Roelxy
新世紀摸魚戰士