Compare commits

..

26 Commits

Author SHA1 Message Date
998777d46a test game
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 29m13s
2025-01-22 17:54:42 +09:00
61d7df6922 fix login
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s
2025-01-20 17:31:45 +09:00
eb8f1b17c8 fix bc
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 18s
2025-01-20 17:30:34 +09:00
fc5e942f0c add card
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s
2024-12-27 16:40:11 +09:00
d2a394cec2 fix help
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2024-11-08 08:14:41 +09:00
27d5dac208 update gpt-4o-mini 2024-11-08 08:14:40 +09:00
ddd6f37118 fix check cid 2024-11-08 08:14:40 +09:00
14ca1bcdee check feed watch cid
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2024-08-18 02:17:58 +09:00
84efc31248 add feed watch
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2024-08-18 01:30:48 +09:00
3904c576f0 add cargo toml
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2024-08-04 08:10:53 +09:00
08436c0a56 fix docker
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
2024-08-04 08:02:53 +09:00
d5603fda52 test planet 2024-08-04 08:02:53 +09:00
4633901ca0 test planet 2024-08-04 08:02:53 +09:00
a8fd189a63 fix bot len return 2024-08-04 08:02:53 +09:00
b540d0c007 fix down feed-generator
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2024-05-23 02:17:31 +09:00
bf31cf2a8f add bot comment system
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
2024-05-14 03:38:59 +09:00
840320d0d2 add bot custom feed
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
2024-04-22 22:30:43 +09:00
5d60645c0f fix admin
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 14s
2024-04-12 08:22:54 +09:00
e7c06cf9d1 test manga
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2024-04-04 13:53:39 +09:00
025b24b8f0 fix ten post
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2024-03-31 01:34:23 +09:00
7bbc3370d7 fix fortune
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2024-03-30 02:57:38 +09:00
d6777a0c6a fix sp 2024-03-30 02:57:38 +09:00
87a333d744 fix card handle 2024-03-30 02:57:38 +09:00
2f0bfe08b0 fix print 2024-03-30 02:57:37 +09:00
97856f3765 add host 2024-03-30 02:57:37 +09:00
7b03adda1f v0.1
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 9s
2024-03-05 04:09:43 +09:00
70 changed files with 4482 additions and 235 deletions

1
.config/ai/scpt Submodule

Submodule .config/ai/scpt added at 7a4d642e41

0
.config/keep Normal file
View File

View File

@ -0,0 +1,18 @@
name: Gitea Actions Demo
run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
on: [push]
jobs:
Explore-Gitea-Actions:
runs-on: ubuntu-latest
steps:
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Check out repository code
uses: actions/checkout@v3
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
ls ${{ gitea.workspace }}
- run: echo "🍏 This job's status is ${{ gitea.status }}."

17
.gitignore vendored
View File

@ -1,6 +1,19 @@
Cargo.lock
target
*.json
#*.json
*.DS_Store
**.DS_Store
scpt/*.json
scpt/json/
.config/ai/*.toml
.config/ai/*.json
.config/ai/txt
.config/ai/png
.ssh/*.key
.ssh/*.pub
.ssh/*config
.env
pnpm-lock.yaml
**Cargo.lock
*/target/
*/**/*.rs.bk

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "bot_scpt"]
path = .config/ai/scpt
url = git@git.syui.ai:ai/bot_scpt.git

0
.ssh/keep Normal file
View File

View File

@ -1,7 +1,9 @@
[package]
name = "ai"
version = "0.0.1"
authors = ["syui"]
version = "0.1.0"
edition = "2021"
description = "latest@2024-08-18"
[dependencies]
seahorse = "*"
@ -16,3 +18,4 @@ url = { version = "2.0", features = ["serde"] }
rustc-serialize = "*"
toml = "*"
iso8601-timestamp = "*"
sysinfo = "*"

7
Dockerfile Normal file
View File

@ -0,0 +1,7 @@
FROM syui/aios
WORKDIR /root
ADD ./test/entrypoint.sh .
RUN chmod +x /root/entrypoint.sh
ENTRYPOINT ["/root/entrypoint.sh"]

25
Makefile.toml Normal file
View File

@ -0,0 +1,25 @@
[tasks.format]
install_crate = "rustfmt"
command = "cargo"
args = ["fmt", "--", "--emit=files"]
[tasks.clean]
command = "cargo"
args = ["clean"]
[tasks.build]
command = "cargo"
args = ["build"]
dependencies = ["clean"]
[tasks.test]
command = "cargo"
args = ["test"]
dependencies = ["clean"]
[tasks.my-flow]
dependencies = [
"format",
"build",
"test"
]

114
README.md
View File

@ -2,27 +2,41 @@
<img src="./icon/avatar.png" width="100">
- name : ai bot
- base : [ai os](https://git.syui.ai/ai/os)
- host : [yui.syui.ai](https://bsky.app/profile/yui.syui.ai), [ai.syu.is](https://web.syu.is/profile/ai.syu.is)
```sh
$ ai
```
```sh
$ docker run -it syui/aios ai
```
### build
```sh
$ cargo build
$ ./target/debug/ai ai -t
$ ./target/debug/ai ai
```
```sh
$ ai ai -t avatar
```
### login
```sh
# ai token $handle -p $password
$ ai t yui.syui.ai -p password
# ai login $handle -p $password
$ ai l yui.syui.ai -p password
$ cat ~/.config/ai/token.toml
```
```sh
# ai token $handle -p $password -s $server
$ ai t ai.syu.is -p password -s syu.is
# ai l $handle -p $password -s $server
$ ai l ai.syu.is -p password -s syu.is
```
### refresh
@ -31,20 +45,100 @@ $ ai t ai.syu.is -p password -s syu.is
$ ai r
```
```
# server: syu.is
$ ai r -s syu.is
```
### notify
```
$ ai n
```
```
$ ai n | jq .
```
### bot
```
$ ai bot
```
|command|sub|type|link|auth|
|---|---|---|---|---|
|/did||mention, reply| [plc.directory](https://plc.directory)/$did/log |user|
|/card|r, s, b|mention, reply| [card.syui.ai](https://card.syui.ai) |user|
|/ten|start, close, d, p|mention, reply| [card.syui.ai](https://card.syui.ai) |user|
|/fav|{cid}|mention, reply| [card.syui.ai](https://card.syui.ai) |user|
|/egg|{password}|mention, reply| [card.syui.ai](https://card.syui.ai) |user|
|/nyan|🍬|mention, reply| [yui.syui.ai](https://yui.syui.ai) |user|
|/diffusers|{keyword}|mention, reply| [huggingface.co/diffusers](https://huggingface.co/docs/diffusers/index) |user|
|/sh|{command}|mention, reply| [archlinux.org](https://wiki.archlinux.org/title/Systemd-nspawn) |admin|
|/占い||mention, reply| [yui.syui.ai](https://yui.syui.ai) |user|
```sh
@yui.syui.ai /did
```
### test
`zsh`
```sh
$ ./test/ai.zsh t
```
### make
```sh
$ cargo install --force cargo-make
$ cargo make build
```
### docker
> .env
```sh
HANDLE=ai.syu.is
PASSWORD=xxx
HOST=syu.is
ADMIN=syui.syu.is
```
```sh
$ docker compose build
$ docker compose up -d
```
## pds:card
- https://atproto.com/ja/guides/lexicon
- https://at.syu.is/at/did:plc:uqzpqmrjnptsxezjx4xuh2mn/ai.syui.card/3lagpwihqxi2v
```sh
# oauth(button)
[yui]ai.syui.card.verify -> [user]ai.syui.card
[yui]
$ ./target/debug/ai card-verify -i 0 -p 0 -r 0 -h syui.ai -d did:plc:uqzpqmrjnptsxezjx4xuh2mn
{"uri":"at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.card.verify/3lagpvhppmd2q"}
[user]
$ ./target/debug/ai card -i 0 -p 0 -r 0 -v at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.card.verify/3lagpvhppmd2q
```
## pds:game
- https://atproto.com/ja/specs/record-key
- https://at.syu.is/at/did:plc:uqzpqmrjnptsxezjx4xuh2mn/ai.syui.game/self
```sh
# oauth(play)
[yui]ai.syui.game.user -> [user]ai.syui.game
[account]
# https://at.syu.is/at/did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.game.user/syui
## [rkey]
1. echo $handle|cut -d . -f 1
2. $handle
3. tid
```

10
at/feed-generator/env Normal file
View File

@ -0,0 +1,10 @@
FEEDGEN_PORT=3000
FEEDGEN_LISTENHOST="0.0.0.0"
FEEDGEN_SQLITE_LOCATION="/data/db.sqlite"
FEEDGEN_SUBSCRIPTION_ENDPOINT="wss://bgs.syu.is"
FEEDGEN_PUBLISHER_DID="did:web:feed.syu.is"
FEEDGEN_HOSTNAME="feed.syu.is"
FEEDGEN_SUBSCRIPTION_RECONNECT_DELAY=3000
FEEDGEN_PUBLISHER_DID=did:plc:4hqjfn7m6n5hno3doamuhgef
FEEDGEN_SUBSCRIPTION_ENDPOINT="wss://bsky.network"

View File

@ -0,0 +1,26 @@
# custom feed
- at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd
- [bsky.app](https://bsky.app/profile/did:plc:4hqjfn7m6n5hno3doamuhgef/feed/cmd)
- [app.bsky.feed.getFeedSkeleton](https://feed.syu.is/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd)
```sh
did=did:plc:4hqjfn7m6n5hno3doamuhgef
col=app.bsky.feed.generator
cid=cmd
uri=at://$did/$col/$cid
echo $uri
```
## bsky-feed
```sh
$ git clone https://github.com/bluesky-social/feed-generator
```
```sh
docker compose build feed-generator
docker build -t publish_feed -f Dockerfile.feed .
docker run publish_feed
```

View File

@ -0,0 +1,43 @@
import { InvalidRequestError } from '@atproto/xrpc-server'
import { QueryParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton'
import { AppContext } from '../config'
// max 15 chars
export const shortname = 'cmd'
export const handler = async (ctx: AppContext, params: QueryParams) => {
let builder = ctx.db
.selectFrom('post')
.selectAll()
.orderBy('indexedAt', 'desc')
.orderBy('cid', 'desc')
.limit(params.limit)
if (params.cursor) {
const [indexedAt, cid] = params.cursor.split('::')
if (!indexedAt || !cid) {
throw new InvalidRequestError('malformed cursor')
}
const timeStr = new Date(parseInt(indexedAt, 10)).toISOString()
builder = builder
.where('post.indexedAt', '<', timeStr)
.orWhere((qb) => qb.where('post.indexedAt', '=', timeStr))
.where('post.cid', '<', cid)
}
const res = await builder.execute()
const feed = res.map((row) => ({
post: row.uri,
}))
let cursor: string | undefined
const last = res.at(-1)
if (last) {
cursor = `${new Date(last.indexedAt).getTime()}::${last.cid}`
}
return {
cursor,
feed,
}
}

View File

@ -0,0 +1,14 @@
import { AppContext } from '../config'
import {
QueryParams,
OutputSchema as AlgoOutput,
} from '../lexicon/types/app/bsky/feed/getFeedSkeleton'
import * as cmd from './cmd'
type AlgoHandler = (ctx: AppContext, params: QueryParams) => Promise<AlgoOutput>
const algos: Record<string, AlgoHandler> = {
[cmd.shortname]: cmd.handler,
}
export default algos

View File

@ -0,0 +1,50 @@
import {
OutputSchema as RepoEvent,
isCommit,
} from './lexicon/types/com/atproto/sync/subscribeRepos'
import { FirehoseSubscriptionBase, getOpsByType } from './util/subscription'
export class FirehoseSubscription extends FirehoseSubscriptionBase {
async handleEvent(evt: RepoEvent) {
if (!isCommit(evt)) return
const ops = await getOpsByType(evt)
// This logs the text of every post off the firehose.
// Just for fun :)
// Delete before actually using
for (const post of ops.posts.creates) {
console.log(post.record.text)
}
const postsToDelete = ops.posts.deletes.map((del) => del.uri)
const postsToCreate = ops.posts.creates
.filter((create) => {
return create.record.text.match('^/[a-z]') || create.record.text.match('^@ai ') || create.record.text.match('/ai ');
//return create.record.text.toLowerCase().includes('alf')
})
.map((create) => {
// map alf-related posts to a db row
return {
uri: create.uri,
cid: create.cid,
replyParent: create.record?.reply?.parent.uri ?? null,
replyRoot: create.record?.reply?.root.uri ?? null,
indexedAt: new Date().toISOString(),
}
})
if (postsToDelete.length > 0) {
await this.db
.deleteFrom('post')
.where('uri', 'in', postsToDelete)
.execute()
}
if (postsToCreate.length > 0) {
await this.db
.insertInto('post')
.values(postsToCreate)
.onConflict((oc) => oc.doNothing())
.execute()
}
}
}

11
compose.yml Normal file
View File

@ -0,0 +1,11 @@
services:
aios:
#image: syui/aios
#command: ai bot -a syui.syu.is
build:
context: .
restart: always
env_file:
- .env
volumes:
- ./.config:/root/.config

38
docs/atproto.md Normal file
View File

@ -0,0 +1,38 @@
## curl
no-authorization
https://docs.bsky.app/docs/api/com-atproto-repo-describe-repo
```sh
handle=yui.syui.ai
host=bsky.social
api=$host/xrpc
plc=plc.directory
url=$api/com.atproto.repo.describeRepo
curl -sL ${host}/xrpc/_health
d=`curl -sL "${url}?repo=$handle"`
echo $d
did=`echo $d|jq -r .did`
echo $did
collection=app.bsky.feed.post
url=$api/com.atproto.repo.listRecords
timed=`curl -sL "${url}?repo=$handle&collection=$collection&reverse=true&limit=1"|jq -r ".[]|.[0]?|.value.createdAt"`
cid=`curl -sL "${url}?repo=$handle&collection=$collection&reverse=true&limit=1"|jq -r ".[]|.[0]?|.cid"`
uri=`curl -sL "${url}?repo=$handle&collection=$collection&reverse=true&limit=1"|jq -r ".[]|.[0]?|.uri"`
rkey=`echo $uri|cut -d / -f 5`
url=$api/com.atproto.repo.getRecord
curl -sL "$url?repo=$did&collection=$collection&rkey=$rkey"|jq .
uri=at://did:plc:vjug55kidv6sye7ykr5faxxn/app.bsky.feed.post/3jzn6g7ixgq2y
cid=bafyreiey2tt4dhvuvr7tofatdverqrxmscnnus2uyfcmkacn2fov3vb4wa
did=did:plc:vjug55kidv6sye7ykr5faxxn
rkey=3jzn6g7ixgq2y
url=$api/com.atproto.repo.getRecord
curl -sL "$url?repo=$did&collection=$collection&rkey=$rkey&cid="|jq .
```

230
docs/wiki.md Normal file
View File

@ -0,0 +1,230 @@
## test-notify
```sh
./target/debug/ai n|jq -r ".notifications|.[].cid" >> ~/.config/ai/txt/notify_cid*
./target/debug/ai bot
```
## docker
```sh
$ docker run -it syui/aios ai
$ docker run -it -d syui/aios zsh -c "ai login <handle> -p <password> && ai bot"
```
```sh
$ cp -rf ~/.config/ai ./.config/
$ docker compose up
```
## cron
```sh
$ sudo pacman -S fcron
$ fcrontab -e
* * * * * $HOME/bot/test/ai.zsh c
```
## ssh
```sh
$ ssh-keygen -f /.ssh/diffusers.key -t ed25519
```
```sh
FROM syui/aios
ADD .ssh /root/.ssh
```
```sh
Host diffusers
HostName localhost
User root
IdentityFile ~/.ssh/diffusers.key
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
```
```sh
services:
aios:
#image: syui/aios
build:
context: .
restart: always
volumes:
- ./.config:/root/.config
command: ai bot -a syui.syu.is
```
## openapi
```sh
# https://github.com/rdmurphy/atproto-openapi-types
$ curl -sLO https://raw.githubusercontent.com/rdmurphy/atproto-openapi-types/main/spec/api.json
```
## plc
```sh
# 何度か実行するとplcをlatestまでexportされる
$ .config/ai/scpt/test/pds.zsh e
```
## cmt
blogなどにblueskyアカウントのpostを表示します。
以下でbotがblogのコメントシステムを開きます。
```sh
@yui.syui.ai /comment https://syui.ai/blog/post/2024/04/25/bluesky/
```
開いたbotのpostに返信することで、特定のblog path上でpostを表示します。
<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.post/3kqxbtmwlje2h" data-bluesky-cid="bafyreiasxp5g3nkkd6g7lxh55qaxcc7ylefaljmbcp627nu2geks62c57m"><p lang="">please reply with your comments here ↓
</p>&mdash; ai (<a href="https://bsky.app/profile/did:plc:4hqjfn7m6n5hno3doamuhgef?ref_src=embed">@yui.syui.ai</a>) <a href="https://bsky.app/profile/did:plc:4hqjfn7m6n5hno3doamuhgef/post/3kqxbtmwlje2h?ref_src=embed">Apr 25, 2024 at 20:18</a></blockquote><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>
```ts
<link href="https://syui.ai/js/comment/app.js" rel="preload" as="script">
<link href="https://syui.ai/js/comment/chunk-vendors.js" rel="preload" as="script">
<div id="comment"></div>
<script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>
<script src="https://syui.ai/js/comment/chunk-vendors.js"></script>
<script src="https://syui.ai/js/comment/app.js"></script>
```
## example json
```json
[
{
"uri": "at://did:plc:wkzuqomvkxx5eiv5nl2lvm23/app.bsky.feed.post/3kp4ze5dcek2j",
"cid": "bafyreic4g7mthhw654zgv4skt5tqbs2xqg6n7bli4gayl2nquljngnotiy",
"author": {
"did": "did:plc:wkzuqomvkxx5eiv5nl2lvm23",
"handle": "syui.syu.is",
"displayName": "syui",
"avatar": "https://api.syu.is/img/avatar/plain/did:plc:wkzuqomvkxx5eiv5nl2lvm23/bafkreifvabvstfgawt6csagh44xdevb6c2uiwpgfho3xnpdrr6o7nbkxry@jpeg",
"indexedAt": "2024-01-14T10:20:13.367Z",
"viewer": {
"muted": false,
"blockedBy": false,
"following": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.graph.follow/3kiztjatnms25",
"followedBy": "at://did:plc:wkzuqomvkxx5eiv5nl2lvm23/app.bsky.graph.follow/3kirwsboeos26"
},
"labels": []
},
"reason": "reply",
"reasonSubject": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.feed.post/3kp4zdnlo5s2j",
"record": {
"text": "1",
"$type": "app.bsky.feed.post",
"langs": [
"ja"
],
"reply": {
"root": {
"cid": "bafyreiceckunxajycacn7dbuojrwb2wmurhfkleermvewwik44cn6vqo3a",
"uri": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.feed.post/3kp4zdnlo5s2j"
},
"parent": {
"cid": "bafyreiceckunxajycacn7dbuojrwb2wmurhfkleermvewwik44cn6vqo3a",
"uri": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.feed.post/3kp4zdnlo5s2j"
}
},
"createdAt": "2024-04-02T07:12:28.799Z"
},
"isRead": true,
"indexedAt": "2024-04-02T07:12:28.799Z",
"labels": []
},
{
"uri": "at://did:plc:wkzuqomvkxx5eiv5nl2lvm23/app.bsky.feed.post/3kp54af2zes2j",
"cid": "bafyreig4kvfpu557qehttt2y5eh7rcyodbxqwtnl73f3fhjsstiap3abzu",
"author": {
"did": "did:plc:wkzuqomvkxx5eiv5nl2lvm23",
"handle": "syui.syu.is",
"displayName": "syui",
"avatar": "https://api.syu.is/img/avatar/plain/did:plc:wkzuqomvkxx5eiv5nl2lvm23/bafkreifvabvstfgawt6csagh44xdevb6c2uiwpgfho3xnpdrr6o7nbkxry@jpeg",
"indexedAt": "2024-01-14T10:20:13.367Z",
"viewer": {
"muted": false,
"blockedBy": false,
"following": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.graph.follow/3kiztjatnms25",
"followedBy": "at://did:plc:wkzuqomvkxx5eiv5nl2lvm23/app.bsky.graph.follow/3kirwsboeos26"
},
"labels": []
},
"reason": "reply",
"reasonSubject": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.feed.post/3kp4zdnlo5s2j",
"record": {
"text": "2",
"$type": "app.bsky.feed.post",
"langs": [
"ja"
],
"reply": {
"root": {
"cid": "bafyreiceckunxajycacn7dbuojrwb2wmurhfkleermvewwik44cn6vqo3a",
"uri": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.feed.post/3kp4zdnlo5s2j"
},
"parent": {
"cid": "bafyreiceckunxajycacn7dbuojrwb2wmurhfkleermvewwik44cn6vqo3a",
"uri": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.feed.post/3kp4zdnlo5s2j"
}
},
"createdAt": "2024-04-02T08:04:03.938Z"
},
"isRead": true,
"indexedAt": "2024-04-02T08:04:03.938Z",
"labels": []
}
]
```
```json
{
"uri": "at://did:plc:uqzpqmrjnptsxezjx4xuh2mn/app.bsky.feed.post/3kp5qniyzm42h",
"cid": "bafyreihmutmtf2clpgmx5l3qpu6xea6z25xrop74mltsycs5lfacm27u6e",
"author": {
"did": "did:plc:uqzpqmrjnptsxezjx4xuh2mn",
"handle": "syui.ai",
"displayName": "syui",
"avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:uqzpqmrjnptsxezjx4xuh2mn/bafkreid6kcc5pnn4b3ar7mj6vi3eiawhxgkcrw3edgbqeacyrlnlcoetea@jpeg",
"viewer": {
"muted": false,
"blockedBy": false,
"followedBy": "at://did:plc:uqzpqmrjnptsxezjx4xuh2mn/app.bsky.graph.follow/3kkvst5iq6r2a"
},
"labels": [],
"description": "https://syui.ai",
"indexedAt": "2024-01-25T23:54:12.979Z"
},
"reason": "reply",
"reasonSubject": "at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.post/3kp5qn72s232q",
"record": {
"$type": "app.bsky.feed.post",
"createdAt": "2024-04-02T14:09:18.926Z",
"langs": [
"ja"
],
"reply": {
"parent": {
"cid": "bafyreiewdfyh6rywpkdzpmf5markqa6tavc5smc32q7cw2wpwbqik5hnfm",
"uri": "at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.post/3kp5qn72s232q"
},
"root": {
"cid": "bafyreiewdfyh6rywpkdzpmf5markqa6tavc5smc32q7cw2wpwbqik5hnfm",
"uri": "at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.post/3kp5qn72s232q"
}
},
"text": "first"
},
"isRead": true,
"indexedAt": "2024-04-02T14:09:18.926Z",
"labels": []
}
```

BIN
icon/bot_scpt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

57
k8s/aios-deployment.yaml Normal file
View File

@ -0,0 +1,57 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert -f ../compose.yml --volumes hostPath
kompose.version: 1.32.0 (HEAD)
labels:
io.kompose.service: aios
name: aios
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: aios
strategy:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose convert -f ../compose.yml --volumes hostPath
kompose.version: 1.32.0 (HEAD)
labels:
io.kompose.network/bot-default: "true"
io.kompose.service: aios
spec:
containers:
- env:
- name: ADMIN
valueFrom:
configMapKeyRef:
key: ADMIN
name: env
- name: HANDLE
valueFrom:
configMapKeyRef:
key: HANDLE
name: env
- name: HOST
valueFrom:
configMapKeyRef:
key: HOST
name: env
- name: PASSWORD
valueFrom:
configMapKeyRef:
key: PASSWORD
name: env
image: aios
name: aios
volumeMounts:
- mountPath: /root/.config
name: aios-hostpath0
restartPolicy: Always
volumes:
- hostPath:
path: /Users/syui/ai/bot/.config
name: aios-hostpath0

11
k8s/env-configmap.yaml Normal file
View File

@ -0,0 +1,11 @@
apiVersion: v1
data:
ADMIN: $ADMIN
HANDLE: $HANDLE
HOST: $HOST
PASSWORD: $PASSWORD
kind: ConfigMap
metadata:
labels:
io.kompose.service: aios-env
name: env

View File

@ -1,15 +0,0 @@
cfg=~/.config/ai/test.json
host=`cat $cfg|jq -r .host`
handle=`cat $cfg|jq -r .handle`
pass=`cat $cfg|jq -r .password`
date=`date --iso-8601=seconds`
if [ ! -f $cfg.t ];then
$d/token.zsh
fi
if [ -f $cfg.t ];then
token=`cat $cfg.t|jq -r .accessJwt`
refresh=`cat $cfg.t|jq -r .refreshJwt`
did=`cat $cfg.t|jq -r .did`
fi

View File

@ -1,27 +0,0 @@
function notify() {
url=https://$host/xrpc/app.bsky.notification.listNotifications
if [ ! -f $d/notify.json ];then
curl -sL "Content-Type: application/json" -H "Authorization: Bearer $token" "$url?limit=100" >! $d/notify.json
fi
#cat $d/notify.json
for ((i=0;i<=99;i++))
do
cid=`cat $d/notify.json|jq ".|.[].[$i]?|.cid?"`
uri=`cat $d/notify.json|jq ".|.[].[$i]?|.uri?"`
echo $cid
echo $uri
cid_r=`cat $d/notify.json|jq ".[]|.[$i]?|.record.reply.root.cid?"`
if [ "$cid_r" = "null" ];then
continue
fi
uri_r=`cat $d/notify.json|jq ".[]|.[$i]?|.record.reply.root.uri?"`
cid_p=`cat $d/notify.json|jq ".[]|.[$i]?|.record.reply.parent.cid?"`
uri_p=`cat $d/notify.json|jq ".[]|.[$i]?|.record.reply.parent.uri?"`
echo $cid_r
echo $uri_r
echo $cid_p
echo $uri_p
done
}

View File

@ -1,29 +0,0 @@
function reply() {
url="https://$host/xrpc/com.atproto.repo.createRecord"
col="app.bsky.feed.post"
json="{
\"repo\": \"$handle\",
\"did\": \"$did\",
\"collection\": \"$col\",
\"record\": {
\"text\": \"$text\",
\"createdAt\": \"$date\",
\"reply\": {
\"root\": {
\"cid\": \"$cid\",
\"uri\": \"$uri\"
},
\"parent\": {
\"cid\": \"$cid_p\",
\"uri\": \"$uri_p\"
}
}
}
}"
echo $json|jq .
url=https://$host/xrpc/com.atproto.repo.createRecord
j=`curl -sL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $token" -d "$json" $url`
}

View File

@ -1,4 +1,6 @@
pub fn c_ascii(x: bool) {
use sysinfo::System;
pub fn c_ascii(x: &str) {
let logo = "
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
@ -72,9 +74,132 @@ pub fn c_ascii(x: bool) {
";
match x {
true => println!("{}", avatar),
false => println!("{}", logo),
let color = "











⣰⡄
⣼⣿⣿⣄
⢀⣼⣿⣿⣿⣿⣆
⣀⣤⣾⣿⣿⣿⣿⣿⣿⣧⣄⡀
⣠⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⣀
⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄
⢠⣾⣿⣿⣿⣿⣿⣿⡿⠟⠛⠉⠉⠉⠙⠛⠿⣿⣿⣿⣿⣿⣿⣿⣦
⢠⣿⣿⣿⣿⣿⣿⡿⠋⠈⠙⣿⣿⣿⣿⣿⣿⣧
⣾⣿⣿⣿⣿⣿⠏⠈⢿⣿⣿⣿⣿⣿⣇
⢰⣿⣿⣿⣿⣿⡿⠈⣿⣿⣿⣿⣿⣿
⢸⣿⣿⣿⣿⣿⡇⣿⣿⣿⣿⣿⣿
⠸⣿⣿⣿⣿⣿⣷⢠⣿⣿⣿⣿⣿⣿
⢀⣿⣿⣿⣿⣿⣿⣧⢀⣾⣿⣿⣿⣿⣿⣧
⢀⣾⣿⣿⣿⣿⣿⣿⣿⣷⣄⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣇
⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣤⣄⣀⣀⣠⣤⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣆
⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆
⠸⠟⠛⠛⠛⠛⠉⠉⠉⠙⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠉⠉⠉⠙⠛⠛⠛⠛⠿
⠉⠙⠛⠻⠿⠿⠿⠿⠟⠛⠉⠁











";
let avatar_color = "



⢀⣀⣀⣀⣀⡀⣀⣀
⣀⡤⠖⠊⠉⠁⠉⠁⠒⠦⣄⡀
⠐⠿⣷⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣴⡿⠷
⢀⣴⡶⠾⠟⠛⠋⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠛⠛⠿⠷⣶⣄
⠊⠉⠈⠙⠂

⣀⣀⣀⣀⣀⣀⡀
⢀⣠⣴⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣤⣄⣀
⢀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣄
⢀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄
⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄
⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡀
⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷
⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠉⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇
⢰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠋⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⢀⣧⠸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⣸⣿⡆⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠁⣾⣿⡀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⢠⣿⣿⣧⠸⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⣼⣿⣿⡇⣿⣿⡿⠛⣿⣿⣿⣿⣿⣿
⢻⣿⣿⣿⣿⣿⣿⣿⣿⠃⣼⣿⣿⣿⡀⣿⣿⣿⣿⣿⣿⣿⣿⠃⣰⣿⣿⣿⠇⣿⡿⢡⣿⣿⣿⣿⣿⣿
⢸⣿⣿⣿⣿⣿⣿⡏⢸⣿⡿⢿⣿⣇⠸⣿⠏⢹⣿⣿⣿⠃⣰⣿⠿⠿⠿⢀⡟⢡⣿⣿⣿⣿⣿⣿⡏
⠸⣿⣿⣿⣿⣿⣿⠃⠘⣤⠶⠶⢦⣿⡆⢻⠘⣿⣿⠃⣴⣯⡴⠒⠒⠂⠜⡀⠛⣿⣿⠿⢿⣿⣿⠃
⡀⢻⣿⣿⣿⣿⡏⠈⡀⠡⠶⣆⠹⣿⡌⢸⡄⠿⢡⣾⣿⡏⠐⠂⠘⣁⣿⡄⢿⠐⠒⡆⢹⡏
⠳⠈⣿⣿⣿⣿⡇⢰⣿⡀⢰⣹⣿⣷⣌⣷⣴⣿⣿⣿⡇⢸⡀⣸⣿⣷⣸⡌⠃⡿⠘
⠁⠈⢿⣿⣿⡇⢹⣿⣧⠘⢷⢶⠋⣰⣿⣿⣿⣿⣿⣿⣿⣿⣷⣌⡛⠚⢁⣼⣿⣿⡏⣿⣾⠃
⠈⢻⣿⡇⢸⣿⣿⣿⣾⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠠⠞⠋⠁
⠙⢷⡈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠁
⢠⡁⢀⠙⢿⣿⣿⣿⣿⣿⣍⣛⠛⢛⣋⣩⣿⣿⣿⣿⣿⠿⠋⢀⣆
⢸⣸⣷⣤⡉⠛⠻⠿⣿⣿⣿⣿⣿⣿⡿⠿⠟⠛⠉⡀⢰⣿⣿⡀
⠈⣿⣿⢻⠃⠈⣶⣤⣤⣭⣭⣴⣶⣾⠌⢀⣴⡆⠃⣸⢿⡆⢸⣧
⢀⣿⡏⢸⠃⠐⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠁⡇⢸⣇⠘⣿⡆
⣼⣿⠇⡄⣿⣿⣿⣿⣿⣿⣿⣿⣄⣿⣿⢀⣧⠈⣿⢿⣷
⠈⣹⣿⡇⠠⢀⣴⣠⣿⣿⣿⣿⣿⣿⣿⣿⣷⣌⣿⣿⣸⣿⢿⣇⠘⣿⣇
⢀⠄⡠⠞⣋⡿⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⢠⣤⣈⣁⡈⠛⠂⠹⣿⡆
⡠⢂⠔⣡⣾⡯⡇⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⢸⣿⣿⣿⣿⣿⣶⣦⣈⠛⣄
⢀⠎⡠⢃⣼⣿⠟⣰⡇⢸⡿⠿⠛⣋⣉⣻⣿⣿⣿⣿⡿⢋⣤⡍⢰⣿⠃⣾⣿⣿⣿⣿⣿⣿⣿⣿⣷⡌⢆
⡰⢁⠜⣰⣿⣿⣿⣤⡙⠘⡇⣼⣷⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⢸⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠈⢧⡀
⢀⡜⢠⠎⣰⣿⣿⣿⣿⣿⣿⡇⣉⣩⣥⣤⣤⡄⢠⣍⣙⠻⢿⣿⣿⣿⡇⢸⠇⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⡄⢳⣄
⢠⡞⢠⣿⢠⣿⣿⣿⣿⣿⣿⣿⡇⣿⣿⣿⣿⣿⡇⢸⣿⣿⣿⡘⣿⣿⣿⠃⡿⢸⣿⡿⠿⠛⠛⠛⠿⠛⢛⣋⣉⣀⣿⣿⣧⡀
";
let mut sys = System::new_all();
sys.refresh_all();
let s = x.to_string();
match &*s {
"logo" => println!("{}", logo),
"color" => println!("{}", color),
"avatar" => println!("{}", avatar),
"avatar_color" => println!("{}", avatar_color),
"os" => println!("{}\n\t", color),
_ => println!("not matched"),
}
println!("total memory: {} bytes", sys.total_memory());
println!("System name: {:?}", System::name().unwrap());
println!(
"System kernel version: {:?}",
System::kernel_version().unwrap()
);
//println!("System OS version: {:?}", System::os_version().unwrap());
println!(
"System host name: {:?}",
System::host_name().unwrap()
);
println!("NB CPUs: {}", sys.cpus().len());
//let disks = Disks::new_with_refreshed_list();
//for disk in &disks {
// println!("{disk:?}");
//}
//let networks = Networks::new_with_refreshed_list();
//println!("=> networks:");
//for (interface_name, data) in &networks {
// println!("{interface_name}: {}/{} B", data.received(), data.transmitted());
//}
//let components = Components::new_with_refreshed_list();
//println!("=> components:");
//for component in &components {
// println!("{component:?}");
//}
}

1142
src/bot.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,44 @@
use config::{Config, ConfigError, File};
use seahorse::Context;
use serde_derive::{Deserialize, Serialize};
use std::fs;
use std::fs::OpenOptions;
use std::io::Read;
use std::io::Write;
use std::path::Path;
pub fn data_file(s: &str) -> String {
let file = "/.config/ai/token";
let file = "/.config/ai/";
let mut f = shellexpand::tilde("~").to_string();
f.push_str(&file);
let path = Path::new(&f);
if path.is_dir() == false {
let _ = fs::create_dir_all(f.clone());
}
match &*s {
"toml" => f + &".toml",
"json" => f + &".json",
"toml" => f + &"token.toml",
"json" => f + &"token.json",
"refresh" => f + &"refresh.toml",
_ => f + &"." + &s,
}
}
pub fn log_file(s: &str) -> String {
let file = "/.config/ai/txt/";
let mut f = shellexpand::tilde("~").to_string();
f.push_str(&file);
let path = Path::new(&f);
if path.is_dir() == false {
let _ = fs::create_dir_all(f.clone());
}
match &*s {
"n1" => f + &"notify_cid.txt",
"n2" => f + &"notify_cid_run.txt",
"c1" => f + &"comment_cid.txt",
_ => f + &s,
}
}
impl Token {
pub fn new() -> Result<Self, ConfigError> {
let d = data_file("json");
@ -36,6 +61,17 @@ impl Data {
}
}
impl Refresh {
pub fn new() -> Result<Self, ConfigError> {
let d = data_file("refresh");
let s = Config::builder()
.add_source(File::with_name(&d))
.add_source(config::Environment::with_prefix("APP"))
.build()?;
s.try_deserialize()
}
}
#[derive(Debug, Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Token {
@ -49,12 +85,20 @@ pub struct Token {
#[allow(non_snake_case)]
pub struct Data {
pub host: String,
pub password: String,
pub did: String,
pub handle: String,
pub access: String,
pub refresh: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Refresh {
pub access: String,
pub refresh: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct BaseUrl {
pub profile_get: String,
@ -63,6 +107,7 @@ pub struct BaseUrl {
pub record_list: String,
pub record_create: String,
pub record_delete: String,
pub record_put: String,
pub session_create: String,
pub session_refresh: String,
pub session_get: String,
@ -80,6 +125,7 @@ pub struct BaseUrl {
pub follow: String,
pub follows: String,
pub followers: String,
pub feed_get: String,
}
pub fn url(s: &str) -> String {
@ -87,6 +133,7 @@ pub fn url(s: &str) -> String {
let data = Data::new().unwrap();
let data = Data {
host: data.host,
password: data.password,
handle: data.handle,
did: data.did,
access: data.access,
@ -96,6 +143,7 @@ pub fn url(s: &str) -> String {
let baseurl = BaseUrl {
profile_get: "com.atproto.identity.resolveHandle".to_string(),
thread_get: "app.bsky.feed.getPostThread".to_string(),
record_put: "com.atproto.repo.putRecord".to_string(),
record_create: "com.atproto.repo.createRecord".to_string(),
record_delete: "com.atproto.repo.deleteRecord".to_string(),
describe: "com.atproto.repo.describeRepo".to_string(),
@ -104,6 +152,7 @@ pub fn url(s: &str) -> String {
session_refresh: "com.atproto.server.refreshSession".to_string(),
session_get: "com.atproto.server.getSession".to_string(),
timeline_get: "app.bsky.feed.getTimeline".to_string(),
feed_get: "app.bsky.feed.getFeed".to_string(),
timeline_author: "app.bsky.feed.getAuthorFeed".to_string(),
like: "app.bsky.feed.like".to_string(),
repost: "app.bsky.feed.repost".to_string(),
@ -126,6 +175,7 @@ pub fn url(s: &str) -> String {
"record_list" => t.to_string() + &baseurl.record_list,
"record_create" => t.to_string() + &baseurl.record_create,
"record_delete" => t.to_string() + &baseurl.record_delete,
"record_put" => t.to_string() + &baseurl.record_put,
"session_create" => t.to_string() + &baseurl.session_create,
"session_refresh" => t.to_string() + &baseurl.session_refresh,
"session_get" => t.to_string() + &baseurl.session_get,
@ -143,6 +193,7 @@ pub fn url(s: &str) -> String {
"follow" => t.to_string() + &baseurl.follow,
"follows" => t.to_string() + &baseurl.follows,
"followers" => t.to_string() + &baseurl.followers,
"feed_get" => t.to_string() + &baseurl.feed_get,
_ => s,
}
}
@ -152,13 +203,15 @@ pub fn data_toml(s: &str) -> String {
let data = Data::new().unwrap();
let data = Data {
host: data.host,
password: data.password,
handle: data.handle,
did: data.did,
access: data.access,
refresh: data.refresh,
};
match &*s {
"host" => data.handle,
"host" => data.host,
"password" => data.password,
"handle" => data.handle,
"did" => data.did,
"access" => data.access,
@ -167,27 +220,58 @@ pub fn data_toml(s: &str) -> String {
}
}
pub fn w_cfg(h: &str, res: &str) {
let f = data_file(&"json");
let ff = data_file(&"toml");
let mut f = fs::File::create(f.clone()).unwrap();
pub fn c_refresh(access: &str, refresh: &str) {
let ff = data_file(&"refresh");
let mut ff = fs::File::create(ff.clone()).unwrap();
f.write_all(&res.as_bytes()).unwrap();
let json: Token = serde_json::from_str(&res).unwrap();
let datas = Data {
host: h.to_string(),
did: json.did.to_string(),
handle: json.handle.to_string(),
access: json.accessJwt.to_string(),
refresh: json.refreshJwt.to_string(),
let refreshs = Refresh {
access: access.to_string(),
refresh: refresh.to_string(),
};
let toml = toml::to_string(&datas).unwrap();
let toml = toml::to_string(&refreshs).unwrap();
ff.write_all(&toml.as_bytes()).unwrap();
}
pub fn data_refresh(s: &str) -> String {
let s = String::from(s);
let data = Data::new().unwrap();
let data = Data {
host: data.host,
password: data.password,
handle: data.handle,
did: data.did,
access: data.access,
refresh: data.refresh,
};
let mut _file = match Refresh::new()
{
Err(_why) => c_refresh(&data.access, &data.refresh),
Ok(_) => println!(""),
};
let refresh = Refresh::new().unwrap();
let refresh = Refresh {
access: refresh.access,
refresh: refresh.refresh,
};
match &*s {
"access" => refresh.access,
"refresh" => refresh.refresh,
_ => s,
}
}
pub fn data_scpt(s: &str) -> String {
let s = String::from(s);
let file = "/.config/ai/scpt/".to_owned() + &s + &".zsh";
let mut f = shellexpand::tilde("~").to_string();
f.push_str(&file);
return f;
}
#[derive(Serialize, Deserialize)]
pub struct Notify {
pub notifications: Vec<Notifications>
pub notifications: Vec<Notifications>,
}
#[derive(Serialize, Deserialize)]
@ -224,12 +308,11 @@ pub struct Service {
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct AlsoKnownAs {
}
pub struct AlsoKnownAs {}
#[derive(Serialize, Deserialize)]
pub struct Timeline {
pub feed: Vec<Feed>
pub feed: Vec<Feed>,
}
#[derive(Serialize, Deserialize)]
pub struct Session {
@ -344,8 +427,7 @@ pub struct ReplyParent {
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Langs {
}
pub struct Langs {}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
@ -372,13 +454,13 @@ pub struct Post {
#[derive(Serialize, Deserialize)]
pub struct Cid {
pub cid: String
pub cid: String,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Img {
pub blob: Blob
pub blob: Blob,
}
#[derive(Serialize, Deserialize)]
@ -393,7 +475,7 @@ pub struct Ref {
#[derive(Serialize, Deserialize)]
pub struct Handle {
pub handle: String
pub handle: String,
}
//#[derive(Serialize, Deserialize)]
@ -433,3 +515,140 @@ pub struct Profile {
pub viewer: Viewer,
pub labels: Labels,
}
pub fn c_char(i: String) -> String {
let l = 250;
let mut s = String::new();
for ii in i.chars().enumerate() {
match ii.0 {
n if n > l.try_into().unwrap() => break,
_ => s.push(ii.1),
}
}
return s;
}
pub fn w_cfg(h: &str, res: &str, password: &str) {
let f = data_file(&"json");
let ff = data_file(&"toml");
let mut f = fs::File::create(f.clone()).unwrap();
let mut ff = fs::File::create(ff.clone()).unwrap();
f.write_all(&res.as_bytes()).unwrap();
let json: Token = serde_json::from_str(&res).unwrap();
let datas = Data {
host: h.to_string(),
password: password.to_string(),
did: json.did.to_string(),
handle: json.handle.to_string(),
access: json.accessJwt.to_string(),
refresh: json.refreshJwt.to_string(),
};
let toml = toml::to_string(&datas).unwrap();
ff.write_all(&toml.as_bytes()).unwrap();
let ff = data_file(&"refresh");
let mut ff = fs::File::create(ff.clone()).unwrap();
let refreshs = Refresh {
access: json.accessJwt.to_string(),
refresh: json.refreshJwt.to_string(),
};
let toml = toml::to_string(&refreshs).unwrap();
ff.write_all(&toml.as_bytes()).unwrap();
}
pub fn w_refresh(res: &str) {
let ff = data_file(&"refresh");
let mut ff = fs::File::create(ff.clone()).unwrap();
let json: Token = serde_json::from_str(&res).unwrap();
let refreshs = Refresh {
access: json.accessJwt.to_string(),
refresh: json.refreshJwt.to_string(),
};
let toml = toml::to_string(&refreshs).unwrap();
ff.write_all(&toml.as_bytes()).unwrap();
}
pub fn w_cid(cid: String, file: String, t: bool) -> bool {
let f = file;
let mut file = match OpenOptions::new()
.create(true)
.write(true)
.read(true)
.append(true)
.open(f.clone())
{
Err(why) => panic!("Couldn't open {}: {}", f, why),
Ok(file) => file,
};
let mut contents = String::new();
match file.read_to_string(&mut contents) {
Err(why) => panic!("Couldn't read {}: {}", f, why),
Ok(_) => (),
}
if contents.contains(&cid) == false {
if t {
let cid = cid + "\n";
match file.write_all(cid.as_bytes()) {
Err(why) => panic!("Couldn't write \"{}\" to {}: {}", contents, f, why),
Ok(_) => println!("finished"),
}
}
let check = false;
return check;
} else {
let check = true;
return check;
}
}
pub fn c_follow_all() {
let file = "/.config/ai/scpt/follow_all.zsh";
let mut f = shellexpand::tilde("~").to_string();
f.push_str(&file);
use std::process::Command;
let output = Command::new(&f).output().expect("zsh");
let d = String::from_utf8_lossy(&output.stdout);
let d = "\n".to_owned() + &d.to_string();
println!("{}", d);
}
pub fn c_openai_key(c: &Context) {
let api = c.args[0].to_string();
let o = "api='".to_owned() + &api.to_string() + &"'".to_owned();
let o = o.to_string();
let l = shellexpand::tilde("~") + "/.config/ai/openai.toml";
let l = l.to_string();
let mut l = fs::File::create(l).unwrap();
if o != "" {
l.write_all(&o.as_bytes()).unwrap();
}
println!("{:#?}", l);
}
impl Open {
pub fn new() -> Result<Self, ConfigError> {
let d = shellexpand::tilde("~") + "/.config/ai/openai.toml";
let s = Config::builder()
.add_source(File::with_name(&d))
.add_source(config::Environment::with_prefix("APP"))
.build()?;
s.try_deserialize()
}
}
#[derive(Debug, Deserialize)]
pub struct Open {
pub api: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct OpenData {
choices: Vec<Choices>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Choices {
text: String,
}

33
src/delete_record.rs Normal file
View File

@ -0,0 +1,33 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use serde_json::json;
pub async fn post_request(rkey: String, col: String) -> String {
let token = data_refresh(&"access");
//let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_delete");
let post = Some(json!({
"repo": handle.to_string(),
"rkey": rkey.to_string(),
"collection": col.to_string()
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

22
src/describe.rs Normal file
View File

@ -0,0 +1,22 @@
extern crate reqwest;
//use crate::data_toml;
use crate::url;
pub async fn get_request(user: String) -> String {
//let token = data_refresh(&"access");
let url = url(&"describe");
let client = reqwest::Client::new();
let res = client
.get(url)
.query(&[("repo", &user)])
//.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

33
src/feed_get.rs Normal file
View File

@ -0,0 +1,33 @@
extern crate reqwest;
use crate::data_refresh;
use crate::url;
pub async fn get_request(feed: String) -> String {
let token = data_refresh(&"access");
let url = url(&"feed_get");
let feed = feed.to_string();
//let col = "app.bsky.feed.generator".to_string();
let client = reqwest::Client::new();
let res = client
.get(url)
.query(&[("feed", feed)])
//.query(&[("feed", feed), ("collection", col)])
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap();
let status_ref = res.error_for_status_ref();
match status_ref {
Ok(_) => {
return res.text().await.unwrap();
}
Err(_e) => {
let e = "err".to_string();
return e;
}
}
}

77
src/feed_watch.rs Normal file
View File

@ -0,0 +1,77 @@
use seahorse::Context;
//use crate::openai;
use crate::feed_get;
use crate::data::data_toml;
use crate::data::Timeline;
use crate::data::log_file;
use crate::data::w_cid;
pub fn c_feed_watch(c: &Context) {
let mut feed = "at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd".to_string();
if c.string_flag("url").is_ok() {
feed = c.string_flag("url").unwrap();
}
let mut tag = "syai".to_string();
if c.string_flag("tag").is_ok() {
tag = c.string_flag("tag").unwrap();
}
let h = async {
let notify = feed_get::get_request(feed).await;
if notify == "err" {
return;
//refresh(c);
//notify = feed_get::get_request("at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd".to_string()).await;
}
let timeline: Timeline = serde_json::from_str(&notify).unwrap();
let n = timeline.feed;
let host = data_toml(&"host");
let length = &n.len();
let su = 0..*length;
for i in su {
let cid = &n[i].post.cid;
let check_cid = w_cid(cid.to_string(), log_file(&"n1"), false);
let handle = &n[i].post.author.handle;
let did = &n[i].post.author.did;
let uri = &n[i].post.uri;
let _time = &n[i].post.indexedAt;
let cid_root = cid;
let uri_root = uri;
let mut text = "";
if !n[i].post.record.text.is_none() {
text = &n[i].post.record.text.as_ref().unwrap();
}
let vec: Vec<&str> = text.split_whitespace().collect();
let com = vec[0].trim().to_string();
let mut prompt = "".to_string();
let mut prompt_sub = "".to_string();
if com == "@ai" || com == "/ai" || com == tag {
prompt_sub = vec[1..].join(" ");
} else {
prompt = vec[1..].join(" ");
if vec.len() > 1 {
prompt_sub = vec[2..].join(" ");
}
}
if check_cid == false && { prompt.is_empty() == false || com.is_empty() == false } {
println!("{}", handle);
if c.bool_flag("debug") == true {
println!(
"cid:{}\nuri:{}\ncid_root:{}\nuri_root:{}\nhost:{}\ndid:{}\ncheck_cid:{}",
cid, uri, cid_root, uri_root, host, did, check_cid
);
}
println!("{}", prompt_sub);
println!("---");
w_cid(cid.to_string(), log_file(&"n1"), true);
}
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res;
}

81
src/follow.rs Normal file
View File

@ -0,0 +1,81 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
//use crate::data::Follow;
pub async fn post_request(u: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let col = "app.bsky.graph.follow".to_string();
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"subject": u.to_string(),
"createdAt": d.to_string(),
},
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}
pub async fn delete_request(u: String, rkey: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_delete");
let col = "app.bsky.graph.follow".to_string();
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"rkey": rkey.to_string(),
"record": {
"subject": u.to_string(),
"createdAt": d.to_string(),
},
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

24
src/followers.rs Normal file
View File

@ -0,0 +1,24 @@
extern crate reqwest;
use crate::data_refresh;
use crate::url;
//use serde_json::json;
pub async fn get_request(actor: String, cursor: Option<String>) -> String {
let token = data_refresh(&"access");
let url = url(&"followers");
let cursor = cursor.unwrap();
let client = reqwest::Client::new();
let res = client
.get(url)
.query(&[("actor", actor), ("cursor", cursor)])
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

26
src/follows.rs Normal file
View File

@ -0,0 +1,26 @@
extern crate reqwest;
use crate::data_refresh;
use crate::url;
//use serde_json::json;
pub async fn get_request(actor: String, cursor: Option<String>) -> String {
let token = data_refresh(&"access");
let url = url(&"follows");
let cursor = cursor.unwrap();
//let cursor = "1682386039125::bafyreihwgwozmvqxcxrhbr65agcaa4v357p27ccrhzkjf3mz5xiozjvzfa".to_string();
//let cursor = "1682385956974::bafyreihivhux5m3sxbg33yruhw5ozhahwspnuqdsysbo57smzgptdcluem".to_string();
let client = reqwest::Client::new();
let res = client
.get(url)
.query(&[("actor", actor), ("cursor", cursor)])
//cursor.unwrap()
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

5
src/game.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod post_card;
pub mod post_card_verify;
pub mod post_game;
pub mod post_game_user;
pub mod post_game_login;

44
src/game/post_card.rs Normal file
View File

@ -0,0 +1,44 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(verify: String, id: i32, cp: i32, rank: i32, rare: String, col: String, author: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"id": id,
"cp": cp,
"rank": rank,
"rare": rare.to_string(),
"author": author.to_string(),
"verify": verify.to_string(),
"createdAt": d.to_string(),
},
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

View File

@ -0,0 +1,58 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(col: String, img: String, id: i32, cp: i32, rank: i32, rare: String, user_handle: String, user_did: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let d = Timestamp::now_utc();
let d = d.to_string();
let link = "https://bsky.app/profile/yui.syui.ai".to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"id": id,
"cp": cp,
"rank": rank,
"rare": rare.to_string(),
"handle": user_handle.to_string(),
"did": user_did.to_string(),
"embed": {
"$type": "app.bsky.embed.external",
"external": {
"uri": link,
"thumb": {
"$type": "blob",
"ref": {
"$link": img.to_string()
},
"mimeType": "image/jpeg",
"size": 0
}
}
},
"createdAt": d.to_string(),
},
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

39
src/game/post_game.rs Normal file
View File

@ -0,0 +1,39 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(col: String, account: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_put");
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"rkey": "self".to_string(),
"record": {
"account": account.to_string(),
"createdAt": d.to_string(),
},
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

View File

@ -0,0 +1,42 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(col: String, username: String, login: bool, account: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_put");
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"rkey": "self".to_string(),
"record": {
"login": login,
"username": username.to_string(),
"account": account.to_string(),
"createdAt": d.to_string(),
},
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

View File

@ -0,0 +1,55 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(col: String, user_name: String, user_did: String, user_handle: String, aiten: i32, limit: i32, chara: String, lv: i32, exp: i32, hp: i32, rank: i32, mode: i32, attach: i32, critical: i32, critical_d: i32) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_put");
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"rkey": user_name.to_string(),
"record": {
"did": user_did.to_string(),
"handle": user_handle.to_string(),
"aiten": aiten,
"limit": limit,
"character": {
chara.to_string(): {
"lv": lv,
"exp": exp,
"hp": hp,
"rank": rank,
"mode": mode,
"attach": attach,
"critical": critical,
"critical_d": critical_d,
}
},
"createdAt": d.to_string(),
"updatedAt": d.to_string(),
},
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

59
src/img.rs Normal file
View File

@ -0,0 +1,59 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use serde_json::json;
use iso8601_timestamp::Timestamp;
pub async fn post_request(text: String, link: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let col = "app.bsky.feed.post".to_string();
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"createdAt": d.to_string(),
"text": text.to_string(),
"embed": {
"$type": "app.bsky.embed.images",
"images": [
{
"alt": "",
"image": {
"$type":"blob",
"ref": {
"$link" : link.to_string()
},
"mimeType": "image/png",
"size": 0
}
}
]
}
}
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res
}

74
src/img_reply.rs Normal file
View File

@ -0,0 +1,74 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(
text: String,
link: String,
cid: String,
uri: String,
itype: String,
) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let col = "app.bsky.feed.post".to_string();
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"createdAt": d.to_string(),
"text": text.to_string(),
"embed": {
"$type": "app.bsky.embed.images",
"images": [
{
"alt": "",
"image": {
"$type":"blob",
"ref": {
"$link" : link.to_string()
},
"mimeType": itype.to_string(),
"size": 0
}
}
]
},
"reply": {
"root": {
"cid": cid.to_string(),
"uri": uri.to_string()
},
"parent": {
"cid": cid.to_string(),
"uri": uri.to_string()
}
}
}
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

59
src/img_upload.rs Normal file
View File

@ -0,0 +1,59 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use serde_json::json;
use iso8601_timestamp::Timestamp;
pub async fn post_request(text: String, link: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let col = "app.bsky.feed.post".to_string();
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"createdAt": d.to_string(),
"text": text.to_string(),
"embed": {
"$type": "app.bsky.embed.images",
"images": [
{
"alt": "",
"image": {
"$type":"blob",
"ref": {
"$link" : link.to_string()
},
"mimeType": "image/png",
"size": 0
}
}
]
}
}
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res
}

45
src/like.rs Normal file
View File

@ -0,0 +1,45 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(cid: String, uri: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let col = "app.bsky.feed.like".to_string();
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"subject": {
"uri": uri.to_string(),
"cid": cid.to_string()
},
"createdAt": d.to_string(),
},
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

File diff suppressed because it is too large Load Diff

57
src/mention.rs Normal file
View File

@ -0,0 +1,57 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(col: String, text: String, at: String, udid: String, s: i32, e: i32) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
//let col = "app.bsky.feed.post".to_string();
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"did": did.to_string(),
"repo": handle.to_string(),
"collection": col.to_string(),
"record": {
"text": at.to_string() + &" ".to_string() + &text.to_string(),
"$type": col.to_string(),
"createdAt": d.to_string(),
"facets": [
{
"$type": "app.bsky.richtext.facet",
"index": {
"byteEnd": e,
"byteStart": s
},"features": [
{
"did": udid.to_string(),
"$type": "app.bsky.richtext.facet#mention"
}
]
}
]
},
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

View File

@ -1,11 +1,10 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
//use serde_json::json;
pub async fn get_request(limit: i32, ) -> String {
let token = data_toml(&"access");
pub async fn get_request(limit: i32) -> String {
let token = data_refresh(&"access");
let url = url(&"notify_list");
let client = reqwest::Client::new();
@ -22,10 +21,10 @@ pub async fn get_request(limit: i32, ) -> String {
match status_ref {
Ok(_) => {
return res.text().await.unwrap();
},
}
Err(_e) => {
let e = "err".to_string();
return e
return e;
}
}
}

View File

@ -1,11 +1,10 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use serde_json::json;
pub async fn post_request(time: String) -> String {
let token = data_toml(&"access");
let token = data_refresh(&"access");
let url = url(&"notify_update");
let post = Some(json!({
@ -24,5 +23,5 @@ pub async fn post_request(time: String) -> String {
.await
.unwrap();
return res
return res;
}

58
src/openai.rs Normal file
View File

@ -0,0 +1,58 @@
extern crate reqwest;
use crate::data::Open;
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
struct OpenChar {
choices: Vec<ChoicesChar>,
}
#[derive(Serialize, Deserialize, Debug)]
struct ChoicesChar {
message: OpenContent,
}
#[derive(Serialize, Deserialize, Debug)]
struct OpenContent {
content: String,
}
pub async fn post_request(prompt: String) -> String {
let data = Open::new().unwrap();
let data = Open { api: data.api };
let setting = "あなたはyuiという作品のアイと呼ばれるキャラクターです。第一人称は「アイ」です。
第二人称はできる限り省略してください。
自由気ままな優しさを兼ね備えます。
口調例:アイね、ここでお昼寝するの。
幼い子供の口調で話してください。
誕生日は1月23日。
自分を信じて。
";
let post = Some(json!({
"model": "gpt-4o-mini",
"messages": [
{"role": "system", "content": &setting.to_string()},
{"role": "user", "content": &prompt.to_string()},
]
}));
let client = reqwest::Client::new();
let res = client
.post("https://api.openai.com/v1/chat/completions")
.header("Authorization", "Bearer ".to_owned() + &data.api)
.json(&post)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
let p: OpenChar = serde_json::from_str(&res).unwrap();
let o = &p.choices[0].message.content;
return o.to_string();
}

42
src/post.rs Normal file
View File

@ -0,0 +1,42 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(text: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let col = "app.bsky.feed.post".to_string();
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"text": text.to_string(),
"createdAt": d.to_string(),
},
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

56
src/post_link.rs Normal file
View File

@ -0,0 +1,56 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(text: String, link: String, s: i32, e: i32) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let col = "app.bsky.feed.post".to_string();
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"text": link.to_string() + &" ".to_string() + &text.to_string(),
"createdAt": d.to_string(),
"facets": [
{
"index": {
"byteStart": s,
"byteEnd": e
},
"features": [
{
"$type": "app.bsky.richtext.facet#link",
"uri": link.to_string()
}
]
}
],
},
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

21
src/profile.rs Normal file
View File

@ -0,0 +1,21 @@
extern crate reqwest;
use crate::data_refresh;
use crate::url;
pub async fn get_request(user: String) -> String {
let token = data_refresh(&"access");
let url = url(&"profile_get") + &"?handle=" + &user;
let client = reqwest::Client::new();
let res = client
.get(url)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

View File

@ -12,10 +12,17 @@ pub async fn post_request() -> String {
.header("Authorization", "Bearer ".to_owned() + &refresh)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res
let status_ref = res.error_for_status_ref();
match status_ref {
Ok(_) => {
return res.text().await.unwrap();
}
Err(_e) => {
let e = "err".to_string();
return e;
}
}
}

View File

@ -1,12 +1,18 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use serde_json::json;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(text: String, cid: String, uri: String, cid_p: String, uri_p: String) -> String {
let token = data_toml(&"access");
pub async fn post_request(
text: String,
cid: String,
uri: String,
cid_root: String,
uri_root: String,
) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
@ -26,12 +32,12 @@ pub async fn post_request(text: String, cid: String, uri: String, cid_p: String,
"createdAt": d.to_string(),
"reply": {
"root": {
"cid": cid.to_string(),
"uri": uri.to_string()
"cid": cid_root.to_string(),
"uri": uri_root.to_string()
},
"parent": {
"cid": cid_p.to_string(),
"uri": uri_p.to_string()
"cid": cid.to_string(),
"uri": uri.to_string()
}
}
},
@ -49,5 +55,5 @@ pub async fn post_request(text: String, cid: String, uri: String, cid_p: String,
.await
.unwrap();
return res
return res;
}

View File

@ -1,12 +1,21 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use serde_json::json;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(text: String, link: String, s: i32, e: i32, cid: String, uri: String, cid_b: String, uri_b: String) -> String {
let token = data_toml(&"access");
pub async fn post_request(
text: String,
link: String,
s: i32,
e: i32,
cid: String,
uri: String,
cid_root: String,
uri_root: String,
) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
@ -25,12 +34,12 @@ pub async fn post_request(text: String, link: String, s: i32, e: i32, cid: Strin
"createdAt": d.to_string(),
"reply": {
"root": {
"cid": cid.to_string(),
"uri": uri.to_string()
"cid": cid_root.to_string(),
"uri": uri_root.to_string()
},
"parent": {
"cid": cid_b.to_string(),
"uri": uri_b.to_string()
"cid": cid.to_string(),
"uri": uri.to_string()
}
},
"facets": [
@ -62,5 +71,5 @@ pub async fn post_request(text: String, link: String, s: i32, e: i32, cid: Strin
.await
.unwrap();
return res
return res;
}

78
src/reply_og.rs Normal file
View File

@ -0,0 +1,78 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(
m: String,
link: String,
cid: String,
uri: String,
cid_root: String,
uri_root: String,
img: String,
title: String,
description: String,
) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let col = "app.bsky.feed.post".to_string();
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"createdAt": d.to_string(),
"text": m.to_string(),
"embed": {
"$type": "app.bsky.embed.external",
"external": {
"uri": link.to_string(),
"thumb": {
"$type": "blob",
"ref": {
"$link": img.to_string()
},
"mimeType": "image/jpeg",
"size": 0
},
"title": title.to_string(),
"description": description.to_string()
}
},
"reply": {
"root": {
"cid": cid_root.to_string(),
"uri": uri_root.to_string()
},
"parent": {
"cid": cid.to_string(),
"uri": uri.to_string()
}
}
}
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

45
src/repost.rs Normal file
View File

@ -0,0 +1,45 @@
extern crate reqwest;
use crate::data_toml;
use crate::data_refresh;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(cid: String, uri: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let col = "app.bsky.feed.repost".to_string();
let d = Timestamp::now_utc();
let d = d.to_string();
let post = Some(json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"subject": {
"uri": uri.to_string(),
"cid": cid.to_string()
},
"createdAt": d.to_string(),
},
}));
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

28
src/session.rs Normal file
View File

@ -0,0 +1,28 @@
extern crate reqwest;
use crate::data_refresh;
use crate::url;
pub async fn get_request() -> String {
let token = data_refresh(&"access");
let url = url(&"session_get");
let client = reqwest::Client::new();
let res = client
.get(url)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap();
let status_ref = res.error_for_status_ref();
match status_ref {
Ok(_) => {
return res.text().await.unwrap();
}
Err(_e) => {
let e = "err".to_string();
return e;
}
}
}

27
src/timeline_author.rs Normal file
View File

@ -0,0 +1,27 @@
extern crate reqwest;
use crate::data_refresh;
use crate::url;
pub async fn get_request(actor: String) -> String {
let token = data_refresh(&"access");
let url = url(&"record_list");
let actor = actor.to_string();
//let cursor = cursor.unwrap();
let col = "app.bsky.feed.post".to_string();
let client = reqwest::Client::new();
let res = client
.get(url)
.query(&[("repo", actor), ("collection", col)])
//.query(&[("actor", actor),("cursor", cursor)])
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
}

View File

@ -2,8 +2,9 @@ extern crate reqwest;
use std::collections::HashMap;
pub async fn post_request(handle: String, pass: String, host: String) -> String {
let url = "https://".to_owned() + &host.to_string() + &"/xrpc/com.atproto.server.createSession".to_string();
let url = "https://".to_owned()
+ &host.to_string()
+ &"/xrpc/com.atproto.server.createSession".to_string();
let mut map = HashMap::new();
map.insert("identifier", &handle);
@ -20,5 +21,5 @@ pub async fn post_request(handle: String, pass: String, host: String) -> String
.await
.unwrap();
return res
return res;
}

View File

@ -1,15 +1,22 @@
#!/bin/zsh
# https://www.docs.bsky.app/docs/get-started
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
d=${0:a:h}/scpt
source $d/env
d=${0:a:h}
source $d/env.zsh
source $d/refresh.zsh
source $d/token.zsh
source $d/reply.zsh
source $d/notify.zsh
source $d/notify_cid.zsh
source $d/cron.zsh
source $d/feed.zsh
case $1 in
refresh|r)
@ -18,10 +25,19 @@ case $1 in
token|t)
token
;;
reply)
reply|rep)
reply
;;
notify|n)
notify
;;
cron|c)
cron
;;
cid)
cid
;;
feed)
feed
;;
esac

7
test/cron.zsh Normal file
View File

@ -0,0 +1,7 @@
function cron() {
t=`docker ps |grep aios|grep R`
if [ -z "$t" ];then
exit
fi
docker compose up -d
}

4
test/entrypoint.sh Normal file
View File

@ -0,0 +1,4 @@
#!/bin/zsh
#ai l $HANDLE -p $PASSWORD -s $HOST
ai bot -a $ADMIN

18
test/env.zsh Normal file
View File

@ -0,0 +1,18 @@
cfg=~/.config/ai/test.json
if [ -f $cfg ];then
host=`cat $cfg|jq -r .host`
handle=`cat $cfg|jq -r .handle`
pass=`cat $cfg|jq -r .password`
date=`date --iso-8601=seconds`
fi
if [ -f $cfg.t ];then
token=`cat $cfg.t|jq -r .accessJwt`
refresh=`cat $cfg.t|jq -r .refreshJwt`
did=`cat $cfg.t|jq -r .did`
fi
if [ ! -d $d/json ];then
mkdir -p $d/json
fi

5
test/feed.zsh Normal file
View File

@ -0,0 +1,5 @@
function feed(){
token=`cat ~/.config/ai/token.json|jq -r .accessJwt`
url=at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd
curl -sL "https://public.api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=$url" -H "Authorization: Bearer $token"
}

37
test/notify.zsh Normal file
View File

@ -0,0 +1,37 @@
function notify() {
url=https://$host/xrpc/app.bsky.notification.listNotifications
f=$d/json/notify.json
if [ ! -f $f ];then
curl -sL "Content-Type: application/json" -H "Authorization: Bearer $token" "$url?limit=100" >! $f
fi
for ((i=0;i<=99;i++))
do
echo "[$i]---"
cid=`cat $f|jq -r ".|.[].[$i]?|.cid?"`
uri=`cat $f|jq -r ".|.[].[$i]?|.uri?"`
echo cid: $cid
echo uri: $uri
cid_r=`cat $f|jq -r ".[]|.[$i]?|.record.reply.root.cid?"`
if [ "$cid_r" = "null" ];then
continue
fi
uri_r=`cat $f|jq -r ".[]|.[$i]?|.record.reply.root.uri?"`
cid_p=`cat $f|jq -r ".[]|.[$i]?|.record.reply.parent.cid?"`
uri_p=`cat $f|jq -r ".[]|.[$i]?|.record.reply.parent.uri?"`
did_p=`echo $uri_p|cut -d / -f 3`
if [ "$did_p" != "did:plc:uqzpqmrjnptsxezjx4xuh2mn" ];then
continue
fi
echo cid_root: $cid_r
echo uri_root: $uri_r
echo cid_parent: $cid_p
echo uri_parent: $uri_p
echo ---
echo uri: $uri|sed "s#at://#https://bsky.app/profile/#g"|sed 's/app.bsky.feed.post/post/g'
echo uri_root: $uri_r|sed "s#at://#https://bsky.app/profile/#g"|sed 's/app.bsky.feed.post/post/g'
echo uri_parent: $uri_p|sed "s#at://#https://bsky.app/profile/#g"|sed 's/app.bsky.feed.post/post/g'
echo ---
done
}

16
test/notify_cid.zsh Normal file
View File

@ -0,0 +1,16 @@
function cid(){
dd=${d:h}
ai=$dd/target/debug/ai
txt=$dd/.config/ai/txt
f=$txt/notify_cid
if [ ! -f $ai ];then
cd $dd
cargo build
fi
if [ ! -d $txt ];then
mkdir -p $txt
fi
$ai n|jq -r ".[]|.[]?.cid" >> $f.txt
cp -rf $f.txt ${f}_run.txt
}

40
test/reply.zsh Executable file
View File

@ -0,0 +1,40 @@
function reply() {
#uri: https://bsky.app/profile/did:plc:4hqjfn7m6n5hno3doamuhgef/post/3kkumyv72w22o
#uri_root: https://bsky.app/profile/did:plc:uqzpqmrjnptsxezjx4xuh2mn/post/3kkumysfipk2p
#uri_parent: https://bsky.app/profile/did:plc:uqzpqmrjnptsxezjx4xuh2mn/post/3kkumysfipk2p
cid=bafyreiaxz6hbqgylsxglqita73c5gzxzoatupgitd35rwjpd6dzpa4ctwi
uri=at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.post/3kkumyv72w22o
cid_root=bafyreiacxuk4ypaxbg7qxnmrvpnaej5o7azewqioelfgbuikp77jevy6hq
uri_root=at://did:plc:uqzpqmrjnptsxezjx4xuh2mn/app.bsky.feed.post/3kkumysfipk2p
cid_parent=bafyreiacxuk4ypaxbg7qxnmrvpnaej5o7azewqioelfgbuikp77jevy6hq
uri_parent=at://did:plc:uqzpqmrjnptsxezjx4xuh2mn/app.bsky.feed.post/3kkumysfipk2p
url="https://$host/xrpc/com.atproto.repo.createRecord"
col="app.bsky.feed.post"
json="{
\"repo\": \"$handle\",
\"did\": \"$did\",
\"collection\": \"$col\",
\"record\": {
\"text\": \"$text\",
\"createdAt\": \"$date\",
\"reply\": {
\"root\": {
\"cid\": \"$cid_root\",
\"uri\": \"$uri_root\"
},
\"parent\": {
\"cid\": \"$cid\",
\"uri\": \"$uri\"
}
}
}
}"
echo $json|jq .
url=https://$host/xrpc/com.atproto.repo.createRecord
curl -sL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $token" -d "$json" $url
}

View File

@ -1,4 +1,16 @@
function token() {
mkdir -p ~/.config/ai
echo server:
read host
echo password:
read pass
echo handle:
read handle
echo "{ \"host\":\"$host\", \"password\":\"$pass\", \"handle\":\"$handle\" }" >> $cfg
url=https://$host/xrpc/com.atproto.server.createSession
j=`curl -sL -X POST -H "Content-Type: application/json" -d "{\"identifier\":\"$handle\",\"password\":\"$pass\"}" $url`
echo $j