Skip to content

Commit

Permalink
Import code.
Browse files Browse the repository at this point in the history
  • Loading branch information
Arnavion committed Oct 18, 2020
1 parent eb92146 commit 033dc7e
Show file tree
Hide file tree
Showing 7 changed files with 321 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/creds
/server
18 changes: 18 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.PHONY: install

install:
if ! /bin/getent group evalr >/dev/null; then \
/sbin/groupadd --system evalr; \
fi

if ! /bin/getent passwd evalr >/dev/null; then \
/sbin/useradd --system --gid evalr --commend 'evalr user' --shell /sbin/nologin --home-dir /var/lib/evalr --create-home evalr; \
fi

cp main.awk main.sh playground.sh /var/lib/evalr/
chown evalr:evalr /var/lib/evalr/{main.awk,main.sh,playground.sh}

mkdir -p /etc/systemd/system/
cp evalr.service /etc/systemd/system/
chown root:root /etc/systemd/system/evalr.service
systemctl daemon-reload
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,40 @@
This is an IRC bot that evaluates code snippets using <https://play.rust-lang.org> and prints the results.


# Dependencies

Required:

- `awk`, specifically `gawk`
- `jq`
- `openssl`

Optional:

- `make` - if you use the Makefile to install the services files


# Install

```sh
# Install binaries and systemd service, and create service user.
sudo make install

# Create creds file. "IRC_PASSWORD" is optional.
cat <<-EOF
IRC_SERVER=...
IRC_NICKNAME=...
IRC_USERNAME=...
IRC_PASSWORD=...
EOF | sudo tee /var/lib/evalr/creds >/dev/null
sudo chown evalr:evalr /var/lib/evalr/creds
sudo chmod 0600 /var/lib/evalr/creds

systemctl enable evalr
systemctl start evalr
```


# License

```
Expand Down
13 changes: 13 additions & 0 deletions evalr.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Unit]
Description=evalr IRC bot for ##rust@freenode

[Service]
ExecStart=/var/lib/evalr/main.sh
WorkingDirectory=~
Restart=always
RestartSec=5s
User=evalr
Group=evalr

[Install]
WantedBy=default.target
106 changes: 106 additions & 0 deletions main.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
BEGIN {
write_line(sprintf("NICK %s", IRC_NICKNAME))
write_line(sprintf("USER %s 0 * :Bot owned by Arnavion", IRC_USERNAME))
if (IRC_PASSWORD != "") {
write_line(sprintf("PRIVMSG nickserv :IDENTIFY %s %s", IRC_USERNAME, IRC_PASSWORD))
}

channel_privmsg_regex = sprintf("^:?%s:", IRC_NICKNAME)
privmsg_regex = sprintf("^:?%s:", IRC_NICKNAME)
highlight_regex = sprintf("^%s:", IRC_NICKNAME)
}

{
gsub(/\r$/, "")
read_line()
}

/^PING\r$/ {
write_line("PONG")
}

/^PING / {
write_line(sprintf("PONG %s", $2))
}

/^[^ ]+ 001 / {
if (IRC_PASSWORD == "") {
write_line("JOIN ##rust")
}
}

/^:NickServ!NickServ@services\. NOTICE / {
if ($3 == IRC_NICKNAME) {
message = ""
for (i = 4; i <= NF; i++) {
message = sprintf("%s%s ", message, $i)
}
gsub(/^:/, "", message)

if (message ~ /^You are now identified for /) {
write_line("JOIN ##rust")
}
}
}

/^[^ ]+ PRIVMSG ##rust / {
if ($4 ~ channel_privmsg_regex) {
printf "=== Acting on request\n" > "/dev/stderr"

split(substr($1, 2), nick_parts, "!")
nick = nick_parts[1]

message = ""
for (i = 4; i <= NF; i++) {
message = sprintf("%s%s ", message, $i)
}
gsub(channel_privmsg_regex, "", message)

command = "./playground.sh"
printf "%s", message |& command
fflush()
close(command, "to")
while ((command |& getline) > 0) {
write_line(sprintf("NOTICE ##rust :%s: %s", nick, $0))
}
close(command)
}
}

/^[^ ]+ PRIVMSG / {
if ($3 == IRC_NICKNAME) {
split(substr($1, 2), nick_parts, "!")
nick = nick_parts[1]

message = ""
for (i = 4; i <= NF; i++) {
message = sprintf("%s%s ", message, $i)
}
gsub(/^:/, "", message)

if (substr(message, 1, 1) != "\x01") {
printf "=== Acting on request\n" > "/dev/stderr"

gsub(highlight_regex, "", message)

command = "./playground.sh"
printf "%s", message |& command
fflush()
close(command, "to")
while ((command |& getline) > 0) {
write_line(sprintf("PRIVMSG %s :%s", nick, $0))
}
close(command)
}
}
}

function read_line() {
printf "<<< %s\n", $0 > "/dev/stderr"
}

function write_line(line) {
printf ">>> %s\n", line > "/dev/stderr"
printf "%s\r\n", line
fflush()
}
16 changes: 16 additions & 0 deletions main.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

set -euo pipefail

. creds

rm -f server
trap 'rm -f server' EXIT
mkfifo server

<server awk \
-f main.awk \
-v "IRC_NICKNAME=$IRC_NICKNAME" \
-v "IRC_USERNAME=$IRC_USERNAME" \
-v "IRC_PASSWORD=${IRC_PASSWORD:-}" |
openssl s_client -connect "$IRC_SERVER" -quiet >server
132 changes: 132 additions & 0 deletions playground.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/bin/bash

set -euo pipefail

message="$(cat)"

attrs=''
channel='stable'
code=''
mode='debug'

while :; do
case "$message" in
' '*)
message="${message# }"
;;

'--beta'|'--beta '*)
channel='beta'
message="${message#--beta }"
;;

'--nightly'|'--nightly '*)
channel='nightly'
message="${message#--nightly }"
;;

'--release'|'--release '*)
mode='release'
message="${message#--release }"
;;

'#!['*)
attr_end="$(expr index "$message" ']')"
attrs="$(printf '%s%s\n' "$attrs" "${message:0:$attr_end}")"
message="${message:$attr_end}"
;;

*)
code="$message"
break
;;
esac
done

if [ -z "$code" ]; then
exit 0
fi

request_body_base="$(
jq -cn \
--arg attrs "$attrs" \
--arg code "$code" \
'{
"code": (
$attrs +
"fn main() { println!(\"{:?}\", {\n\n" +
$code +
"\n\n}); }"
)
}'
)"

if [ -n "${EVALR_TEST:-}" ]; then
jq -cn \
--argjson request_body_base "$request_body_base" \
--arg channel "$channel" \
--arg mode "$mode" \
'$request_body_base + {
"channel": $channel,
"mode": $mode
}'
exit 0
fi

response="$(
jq -cn \
--argjson request_body_base "$request_body_base" \
--arg channel "$channel" \
--arg mode "$mode" \
'$request_body_base + {
"channel": $channel,
"mode": $mode,
"edition": "2018",
"crateType": "bin",
"tests": false,
"backtrace": false
}' |
(
curl \
--max-time 10 \
--silent \
-X POST \
-H 'user-agent: irc.freenode.net/Arnavion' \
-H 'accept: application/json' \
-H 'content-type: application/json' \
--data-binary @- \
'https://play.rust-lang.org/execute' ||
:
)
)"

create_gist=0
if [ -n "$response" ] && (<<< "$response" jq -er '.success' >/dev/null); then
output="$(<<< "$response" jq -r '.stdout')"
else
output="$( (<<< "$response" jq -r '.stderr' | grep '^error') || printf 'unknown error')"
create_gist=1
fi

irc_output="$(<<< "$output" head -n 1 | head -c 64)"
if [ "$irc_output" != "$output" ]; then
create_gist=1
fi

if (( create_gist == 1 )); then
printf '%s ...\n' "$irc_output"

<<< "$request_body_base" curl \
--max-time 10 \
--silent \
-H 'user-agent: irc.freenode.net/Arnavion' \
-H 'accept: application/json' \
-H 'content-type: application/json' \
--data-binary @- \
'https://play.rust-lang.org/meta/gist/' |
jq -r '"https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=\(.id)"'
else
printf '%s\n' "$irc_output"
fi

sleep 2

0 comments on commit 033dc7e

Please sign in to comment.