61490f703253608f3062e36081b369953cb39cb0
[pdt.git] / pdt.sh
1 #!/bin/bash
2 # pdt: post to FSF social media via command line
3 # Copyright (C) 2021 Ian Kelling
4 # SPDX-License-Identifier: AGPL-3.0-or-later
5
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Affero General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Affero General Public License for more details.
15
16 # You should have received a copy of the GNU Affero General Public License
17 # along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19 # This is meant to be sourced.
20
21 PATH="$HOME/.local/bin:$PATH"
22
23 # usage: rbow [-PROFILE_NAME] [COMMAND]
24 #
25 # Wrapper for rainbowstream to use multiple logins and tweet directly
26 # from the command line. PROFILE_NAME can be anything you want.
27 rbow() {
28 local oath f
29 oath=$HOME/.rainbow_oauth
30 if [[ $1 == -* ]]; then
31 rm -f $oath
32 ln -s $oath$1 $oath
33 shift
34 fi
35 source ~/src/rainbowstream/venv/bin/activate
36 if (($#)); then
37 f=$(mktemp)
38 cat >$f <<'EOF'
39 # adds a short delay after each send for more reliable operation
40 set force_conservative 1
41 spawn "rainbowstream"
42 # wait for prompt
43 expect -nocase timeout {exit 1} "@*]: "
44 set cmd [lindex $argv 0];
45 send "$cmd\r\r"
46 expect -nocase timeout {exit 1} "@*]: "
47 # this may not be needed. didnt test
48 sleep 2
49 send "q\r"
50 interact
51 EOF
52 expect $f "$*" || { echo error: expect failed; deactivate; rm -f $f; return 1; }
53 rm -f $f
54 else
55 rainbowstream
56 fi
57 deactivate
58 }
59
60 diaspora() {
61 source ~/src/diaspy/venv/bin/activate
62 python3 ~/src/jan-pona-mute/jan-pona-mute.py "$@" || { deactivate; return 1; }
63 deactivate
64 }
65
66 # usage: toot [-PROFILE_NAME] [TOOT_ARGS]
67 toot() {
68 local account
69 source ~/src/toot/venv/bin/activate
70 if [[ $1 == -* ]]; then
71 account=${1#-}
72 shift
73 command toot activate --quiet $account || { deactivate; return 1; }
74 fi
75 command toot "$@" || { deactivate; return 1; }
76 deactivate
77 }
78
79
80 # post to mastodon + twitter + gnu social post + diaspora
81 #
82 # usage: pdt [--dbd] [-m MEDIA_FILE] [-v VIDEO_PATH] [POST]
83 pdt() {
84 local video media rbow_account gs_account mastodon_account dbd video rbow_arg toot_arg dia_arg gs_arg
85 if [[ $pdttest ]]; then
86 rbow_account=iank
87 gs_account=fsfes
88 mastodon_account=iank@hostux.social
89 else
90 rbow_account=fsf
91 gs_account=fsf
92 mastodon_account=fsf@hostux.social
93 fi
94 dbd=false
95 video=false
96 while [[ $1 == -* ]]; do
97 case $1 in
98 -m)
99 media="$2"
100 shift 2
101 rbow_arg=" --i $media"
102 toot_arg="--media $media"
103 dia_arg="-p $media "
104 gs_arg="-F media=@$media"
105 ;;
106 --dbd)
107 dbd=true
108 rbow_account=dbd
109 gs_account=dbd
110 mastodon_account=endDRM@hostux.social
111 shift
112 ;;
113 -v)
114 video=true
115 media="$2"
116 shift 2
117 ;;
118 esac
119 done
120 if [[ $media ]]; then
121 if [[ ! -e $media ]]; then
122 echo "error: file not found $media"
123 return 1
124 fi
125 if [[ $media == *\ * ]]; then
126 echo "error: file path contains a space. move it to non-space path"
127 return 1
128 fi
129 fi
130 # if we have no argument
131 if (( ! $# )); then
132 read -r -p "input PDT text: " input
133 set -- "$input"
134 fi
135 if [[ $- == *i* ]]; then
136 echo "About to PDT the following line. Press enter to confirm or ctrl-c to quit:"
137 echo "$input"
138 read
139 fi
140 if $video; then
141 local oath
142 oath=$HOME/.rainbow_oauth
143 account=fsf
144 if $dbd; then
145 account=dbd
146 fi
147 rm -f $oath
148 ln -s ${oath}-$account $oath
149 python3 ~/src/video-tweet/async-upload.py "$media" "$*"
150 return
151 fi
152 fails=()
153 if ! rbow -$rbow_account t "$*" $rbow_arg; then
154 fails+=(tweet)
155 fi
156 if ! toot -$mastodon_account post "$*" $toot_arg; then
157 fails+=(toot)
158 fi
159 if ! $dbd; then
160 if ! printf "post %s\n" "${dia_arg}$*" | diaspora; then
161 fails+=(diaspora)
162 fi
163 fi
164 # https://gnusocial.net/doc/twitterapi
165 if ! curl -o /dev/null -sS -u "$gs_account:$(cat ~/.gnusocial_login-$gs_account)" \
166 $gs_arg -F "status=$*" https://status.fsf.org/api/statuses/update.xml; then
167 fails+=(gnu-social)
168 fi
169 if (( ${#fails[@]} )); then
170 printf "$(tput setaf 5)█$(tput sgr0)%.0s" $(eval echo "{1..${COLUMNS:-60}}"); echo
171 echo "FSF ERROR: ${fails[*]} might not have posted" >&2
172 fi
173 }
174
175 pdt-setup() {
176
177 if [[ $pdttest ]]; then
178 mastodon_accounts=(iank)
179 else
180 mastodon_accounts=(fsf endDRM)
181 fi
182
183 mkdir -p ~/src
184 cd ~/src
185 for repo in errhandle rainbowstream diaspy jan-pona-mute video-tweet; do
186 if [[ -e $repo ]]; then
187 cd $repo
188 git fetch
189 git reset --hard origin/master
190 git clean -xfffd
191 cd ..
192 else
193 git clone https://vcs.fsf.org/git/$repo.git
194 fi
195 done
196 source ~/src/errhandle/err
197 cp ~/src/rainbowstream/rainbowstream/colorset/config ~/.rainbow_config.json
198
199 if [[ ! -e ~/.local/bin/pip ]]; then
200 tmp=$(mktemp)
201 wget -O$tmp https://bootstrap.pypa.io/get-pip.py
202 python3 $tmp --user
203 hash -r
204 fi
205
206
207 if [[ ! -e ~/.local/bin/virtualenv ]]; then
208 python3 -m pip install --user virtualenv
209 fi
210
211 cd ~/src/rainbowstream
212 python3 -m virtualenv -p python3 venv
213 source venv/bin/activate
214 python3 -m pip install -r requirements.txt
215 python3 -m pip install -e .
216 deactivate
217
218
219 # This repo's upstream is https://alexschroeder.ch/cgit/diaspy which is
220 # recommended in the install instructions here:
221 # https://alexschroeder.ch/cgit/jan-pona-mute/about/
222 cd ~/src/diaspy
223 python3 -m virtualenv -p python3 venv
224 source venv/bin/activate
225 python3 -m pip install -r requirements.txt
226 python3 -m pip install -e .
227 deactivate
228
229 rm -rf ~/src/toot
230 mkdir -p ~/src/toot
231 cd ~/src/toot
232 python3 -m virtualenv -p python3 venv
233 source venv/bin/activate
234 # pip freeze after a pip install.
235 cat >requirements.txt <<'EOF'
236 beautifulsoup4==4.8.2
237 certifi==2019.11.28
238 chardet==3.0.4
239 idna==2.8
240 requests==2.22.0
241 soupsieve==1.9.5
242 toot==0.25.2
243 urllib3==1.25.8
244 urwid==2.1.0
245 wcwidth==0.1.8
246 EOF
247 python3 -m pip install -r requirements.txt
248 deactivate
249
250
251 if [[ ! -s ~/.config/jan-pona-mute/login ]]; then
252 mkdir ~/.config/jan-pona-mute
253 account=fsf@framasphere.org
254 read -r -p "enter the password for $account > " pass
255 # background: format for this found using the save command
256 cat >~/.config/jan-pona-mute/login <<EOF
257 account $account
258 password $pass
259 login
260 EOF
261 fi
262
263 for account in dbd fsf; do
264 if [[ ! -s ~/.rainbow_oauth-$account ]]; then
265 printf "$(tput setaf 5)█$(tput sgr0)%.0s" $(eval echo "{1..${COLUMNS:-60}}");
266 echo "Please login to $account on twitter in your main browser then press enter. After rainbowstream prompt loads, quit with command q"
267 read -r
268 rbow -$account
269 fi
270 if [[ ! -s ~/.gnusocial_login-$account ]]; then
271 printf "$(tput setaf 5)█$(tput sgr0)%.0s" $(eval echo "{1..${COLUMNS:-60}}");
272 read -r -p "please enter the password for $account@status.fsf.org > " pass
273 touch ~/.gnusocial_login-$account
274 chmod 600 ~/.gnusocial_login-$account
275 printf "%s\n" "$pass" > ~/.gnusocial_login-$account
276 fi
277 done
278
279 for account in ${mastodon_accounts[@]}; do
280 if ! toot activate $account@hostux.social &>/dev/null; then
281 printf "$(tput setaf 5)█$(tput sgr0)%.0s" $(eval echo "{1..${COLUMNS:-60}}");
282 echo "Please login to $account on https://hostux.social in your main browser then press enter."
283 read -r
284 toot login -i hostux.social
285 fi
286 done
287 err-allow
288 }