Compare commits
2 Commits
claude-cod
...
454de01881
Author | SHA1 | Date | |
---|---|---|---|
454de01881
|
|||
57365a41a5
|
@ -1,22 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(find:*)",
|
||||
"Bash(cargo check:*)",
|
||||
"Bash(cargo test)",
|
||||
"Bash(cargo test:*)",
|
||||
"Bash(grep:*)",
|
||||
"Bash(cargo install:*)",
|
||||
"Bash(cargo make:*)",
|
||||
"Bash(cargo:*)",
|
||||
"Bash(ls:*)",
|
||||
"Bash(./target/debug/aibot --help)",
|
||||
"Bash(./target/debug/ai --help)",
|
||||
"Bash(mkdir:*)",
|
||||
"Bash(chmod:*)",
|
||||
"Bash(git checkout:*)",
|
||||
"Bash(git add:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
}
|
Submodule .config/ai/scpt updated: a1905d104b...03a64c3652
16
Cargo.toml
16
Cargo.toml
@ -1,17 +1,9 @@
|
||||
[package]
|
||||
name = "aibot"
|
||||
name = "ai"
|
||||
authors = ["syui"]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "ai.bot - Bluesky AT Protocol Bot"
|
||||
|
||||
[[bin]]
|
||||
name = "aibot"
|
||||
path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "ai"
|
||||
path = "src/alias.rs"
|
||||
description = "latest@2024-08-18"
|
||||
|
||||
[dependencies]
|
||||
seahorse = "*"
|
||||
@ -27,7 +19,3 @@ rustc-serialize = "*"
|
||||
toml = "*"
|
||||
iso8601-timestamp = "*"
|
||||
sysinfo = "*"
|
||||
|
||||
[dev-dependencies]
|
||||
mockito = "1.2"
|
||||
tokio-test = "0.4"
|
||||
|
@ -1,7 +1,9 @@
|
||||
FROM syui/aios
|
||||
ADD .ssh /root/.ssh
|
||||
|
||||
WORKDIR /root
|
||||
ADD ./test/entrypoint.sh .
|
||||
RUN chmod +x /root/entrypoint.sh
|
||||
RUN pacman -Syu bc --noconfirm
|
||||
|
||||
ENTRYPOINT ["/root/entrypoint.sh"]
|
||||
|
@ -17,14 +17,6 @@ command = "cargo"
|
||||
args = ["test"]
|
||||
dependencies = ["clean"]
|
||||
|
||||
[tasks.test-quick]
|
||||
command = "cargo"
|
||||
args = ["test"]
|
||||
|
||||
[tasks.test-verbose]
|
||||
command = "cargo"
|
||||
args = ["test", "--", "--nocapture"]
|
||||
|
||||
[tasks.my-flow]
|
||||
dependencies = [
|
||||
"format",
|
||||
|
27
README.md
27
README.md
@ -108,7 +108,7 @@ $ docker compose build
|
||||
$ docker compose up -d
|
||||
```
|
||||
|
||||
## pds:card
|
||||
## pds
|
||||
|
||||
- https://atproto.com/ja/guides/lexicon
|
||||
- https://at.syu.is/at/did:plc:uqzpqmrjnptsxezjx4xuh2mn/ai.syui.card/3lagpwihqxi2v
|
||||
@ -116,29 +116,4 @@ $ docker compose up -d
|
||||
```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
|
||||
```
|
||||
|
||||
|
56
at/lexicons/ai/syui/card.json
Normal file
56
at/lexicons/ai/syui/card.json
Normal file
@ -0,0 +1,56 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "ai.syui.card",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "record",
|
||||
"description": "Record containing a cards box.",
|
||||
"key": "tid",
|
||||
"record": {
|
||||
"type": "object",
|
||||
"required": ["verify", "createdAt"],
|
||||
"properties": {
|
||||
"id":{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 14,
|
||||
"default": 0
|
||||
},
|
||||
"cp":{
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 5000,
|
||||
"default": 1
|
||||
},
|
||||
"rank":{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 7,
|
||||
"default": 0
|
||||
},
|
||||
"rare": {
|
||||
"type": "string",
|
||||
"enum": ["normal", "super", "ultra", "yui", "ai"],
|
||||
"default": "normal"
|
||||
},
|
||||
"author": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "https://verify...",
|
||||
"default": "https://yui.syui.ai"
|
||||
},
|
||||
"verify": {
|
||||
"type": "string",
|
||||
"format": "at-uri",
|
||||
"description": "at://verify..."
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "datetime",
|
||||
"description": "Client-declared timestamp when this post was originally created."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
62
at/lexicons/ai/syui/card/verify.json
Normal file
62
at/lexicons/ai/syui/card/verify.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "ai.syui.card.verify",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "record",
|
||||
"description": "Record containing a card verify.",
|
||||
"key": "tid",
|
||||
"record": {
|
||||
"type": "object",
|
||||
"required": ["handle", "did", "createdAt"],
|
||||
"properties": {
|
||||
"id":{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 14,
|
||||
"default": 0
|
||||
},
|
||||
"cp":{
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 10000,
|
||||
"default": 1
|
||||
},
|
||||
"rank":{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 7,
|
||||
"default": 0
|
||||
},
|
||||
"rare": {
|
||||
"type": "string",
|
||||
"enum": ["normal", "super", "ultra", "yui", "ai"],
|
||||
"default": "normal"
|
||||
},
|
||||
"handle": {
|
||||
"type": "string",
|
||||
"maxLength": 32,
|
||||
"maxGraphemes": 32
|
||||
},
|
||||
"did": {
|
||||
"type": "string"
|
||||
},
|
||||
"embed": {
|
||||
"type": "union",
|
||||
"refs": [
|
||||
"app.bsky.embed.images",
|
||||
"app.bsky.embed.external",
|
||||
"app.bsky.embed.record",
|
||||
"app.bsky.embed.recordWithMedia"
|
||||
]
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "datetime",
|
||||
"description": "Client-declared timestamp when this post was originally created."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30
at/lexicons/ai/syui/o/comment.json
Normal file
30
at/lexicons/ai/syui/o/comment.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "ai.syui.o.comment",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "record",
|
||||
"description": "Record containing a Frontpage comment.",
|
||||
"key": "tid",
|
||||
"record": {
|
||||
"type": "object",
|
||||
"required": ["content", "createdAt", "post"],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string",
|
||||
"maxLength": 100000,
|
||||
"maxGraphemes": 10000,
|
||||
"description": "The content of the comment."
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "datetime",
|
||||
"description": "Client-declared timestamp when this comment was originally created."
|
||||
},
|
||||
"parent": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
|
||||
"post": { "type": "ref", "ref": "com.atproto.repo.strongRef" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
at/lexicons/ai/syui/o/post.json
Normal file
33
at/lexicons/ai/syui/o/post.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "ai.syui.o.post",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "record",
|
||||
"description": "Record containing a Frontpage post.",
|
||||
"key": "tid",
|
||||
"record": {
|
||||
"type": "object",
|
||||
"required": ["title", "url", "createdAt"],
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"maxLength": 3000,
|
||||
"maxGraphemes": 300,
|
||||
"description": "The title of the post."
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "The URL of the post."
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "datetime",
|
||||
"description": "Client-declared timestamp when this post was originally created."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
at/lexicons/ai/syui/o/vote.json
Normal file
23
at/lexicons/ai/syui/o/vote.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "ai.syui.o.vote",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "record",
|
||||
"description": "Record containing a Frontpage vote.",
|
||||
"key": "tid",
|
||||
"record": {
|
||||
"type": "object",
|
||||
"required": ["subject", "createdAt"],
|
||||
"properties": {
|
||||
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "datetime",
|
||||
"description": "Client-declared timestamp when this vote was originally created."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
126
at/lexicons/com/atproto/repo/applyWrites.json
Normal file
126
at/lexicons/com/atproto/repo/applyWrites.json
Normal file
@ -0,0 +1,126 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "com.atproto.repo.applyWrites",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "procedure",
|
||||
"description": "Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS.",
|
||||
"input": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["repo", "writes"],
|
||||
"properties": {
|
||||
"repo": {
|
||||
"type": "string",
|
||||
"format": "at-identifier",
|
||||
"description": "The handle or DID of the repo (aka, current account)."
|
||||
},
|
||||
"validate": {
|
||||
"type": "boolean",
|
||||
"description": "Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons."
|
||||
},
|
||||
"writes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "union",
|
||||
"refs": ["#create", "#update", "#delete"],
|
||||
"closed": true
|
||||
}
|
||||
},
|
||||
"swapCommit": {
|
||||
"type": "string",
|
||||
"description": "If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations.",
|
||||
"format": "cid"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"properties": {
|
||||
"commit": {
|
||||
"type": "ref",
|
||||
"ref": "com.atproto.repo.defs#commitMeta"
|
||||
},
|
||||
"results": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "union",
|
||||
"refs": ["#createResult", "#updateResult", "#deleteResult"],
|
||||
"closed": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": [
|
||||
{
|
||||
"name": "InvalidSwap",
|
||||
"description": "Indicates that the 'swapCommit' parameter did not match current commit."
|
||||
}
|
||||
]
|
||||
},
|
||||
"create": {
|
||||
"type": "object",
|
||||
"description": "Operation which creates a new record.",
|
||||
"required": ["collection", "value"],
|
||||
"properties": {
|
||||
"collection": { "type": "string", "format": "nsid" },
|
||||
"rkey": { "type": "string", "maxLength": 15 },
|
||||
"value": { "type": "unknown" }
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
"type": "object",
|
||||
"description": "Operation which updates an existing record.",
|
||||
"required": ["collection", "rkey", "value"],
|
||||
"properties": {
|
||||
"collection": { "type": "string", "format": "nsid" },
|
||||
"rkey": { "type": "string" },
|
||||
"value": { "type": "unknown" }
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"type": "object",
|
||||
"description": "Operation which deletes an existing record.",
|
||||
"required": ["collection", "rkey"],
|
||||
"properties": {
|
||||
"collection": { "type": "string", "format": "nsid" },
|
||||
"rkey": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"createResult": {
|
||||
"type": "object",
|
||||
"required": ["uri", "cid"],
|
||||
"properties": {
|
||||
"uri": { "type": "string", "format": "at-uri" },
|
||||
"cid": { "type": "string", "format": "cid" },
|
||||
"validationStatus": {
|
||||
"type": "string",
|
||||
"knownValues": ["valid", "unknown"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"updateResult": {
|
||||
"type": "object",
|
||||
"required": ["uri", "cid"],
|
||||
"properties": {
|
||||
"uri": { "type": "string", "format": "at-uri" },
|
||||
"cid": { "type": "string", "format": "cid" },
|
||||
"validationStatus": {
|
||||
"type": "string",
|
||||
"knownValues": ["valid", "unknown"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"deleteResult": {
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
}
|
72
at/lexicons/com/atproto/repo/createRecord.json
Normal file
72
at/lexicons/com/atproto/repo/createRecord.json
Normal file
@ -0,0 +1,72 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "com.atproto.repo.createRecord",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "procedure",
|
||||
"description": "Create a single new repository record. Requires auth, implemented by PDS.",
|
||||
"input": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["repo", "collection", "record"],
|
||||
"properties": {
|
||||
"repo": {
|
||||
"type": "string",
|
||||
"format": "at-identifier",
|
||||
"description": "The handle or DID of the repo (aka, current account)."
|
||||
},
|
||||
"collection": {
|
||||
"type": "string",
|
||||
"format": "nsid",
|
||||
"description": "The NSID of the record collection."
|
||||
},
|
||||
"rkey": {
|
||||
"type": "string",
|
||||
"description": "The Record Key.",
|
||||
"maxLength": 15
|
||||
},
|
||||
"validate": {
|
||||
"type": "boolean",
|
||||
"description": "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons."
|
||||
},
|
||||
"record": {
|
||||
"type": "unknown",
|
||||
"description": "The record itself. Must contain a $type field."
|
||||
},
|
||||
"swapCommit": {
|
||||
"type": "string",
|
||||
"format": "cid",
|
||||
"description": "Compare and swap with the previous commit by CID."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["uri", "cid"],
|
||||
"properties": {
|
||||
"uri": { "type": "string", "format": "at-uri" },
|
||||
"cid": { "type": "string", "format": "cid" },
|
||||
"commit": {
|
||||
"type": "ref",
|
||||
"ref": "com.atproto.repo.defs#commitMeta"
|
||||
},
|
||||
"validationStatus": {
|
||||
"type": "string",
|
||||
"knownValues": ["valid", "unknown"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": [
|
||||
{
|
||||
"name": "InvalidSwap",
|
||||
"description": "Indicates that 'swapCommit' didn't match current repo commit."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
14
at/lexicons/com/atproto/repo/defs.json
Normal file
14
at/lexicons/com/atproto/repo/defs.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "com.atproto.repo.defs",
|
||||
"defs": {
|
||||
"commitMeta": {
|
||||
"type": "object",
|
||||
"required": ["cid", "rev"],
|
||||
"properties": {
|
||||
"cid": { "type": "string", "format": "cid" },
|
||||
"rev": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
56
at/lexicons/com/atproto/repo/deleteRecord.json
Normal file
56
at/lexicons/com/atproto/repo/deleteRecord.json
Normal file
@ -0,0 +1,56 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "com.atproto.repo.deleteRecord",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "procedure",
|
||||
"description": "Delete a repository record, or ensure it doesn't exist. Requires auth, implemented by PDS.",
|
||||
"input": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["repo", "collection", "rkey"],
|
||||
"properties": {
|
||||
"repo": {
|
||||
"type": "string",
|
||||
"format": "at-identifier",
|
||||
"description": "The handle or DID of the repo (aka, current account)."
|
||||
},
|
||||
"collection": {
|
||||
"type": "string",
|
||||
"format": "nsid",
|
||||
"description": "The NSID of the record collection."
|
||||
},
|
||||
"rkey": {
|
||||
"type": "string",
|
||||
"description": "The Record Key."
|
||||
},
|
||||
"swapRecord": {
|
||||
"type": "string",
|
||||
"format": "cid",
|
||||
"description": "Compare and swap with the previous record by CID."
|
||||
},
|
||||
"swapCommit": {
|
||||
"type": "string",
|
||||
"format": "cid",
|
||||
"description": "Compare and swap with the previous commit by CID."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"commit": {
|
||||
"type": "ref",
|
||||
"ref": "com.atproto.repo.defs#commitMeta"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": [{ "name": "InvalidSwap" }]
|
||||
}
|
||||
}
|
||||
}
|
51
at/lexicons/com/atproto/repo/describeRepo.json
Normal file
51
at/lexicons/com/atproto/repo/describeRepo.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "com.atproto.repo.describeRepo",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "query",
|
||||
"description": "Get information about an account and repository, including the list of collections. Does not require auth.",
|
||||
"parameters": {
|
||||
"type": "params",
|
||||
"required": ["repo"],
|
||||
"properties": {
|
||||
"repo": {
|
||||
"type": "string",
|
||||
"format": "at-identifier",
|
||||
"description": "The handle or DID of the repo."
|
||||
}
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"handle",
|
||||
"did",
|
||||
"didDoc",
|
||||
"collections",
|
||||
"handleIsCorrect"
|
||||
],
|
||||
"properties": {
|
||||
"handle": { "type": "string", "format": "handle" },
|
||||
"did": { "type": "string", "format": "did" },
|
||||
"didDoc": {
|
||||
"type": "unknown",
|
||||
"description": "The complete DID document for this account."
|
||||
},
|
||||
"collections": {
|
||||
"type": "array",
|
||||
"description": "List of all the collections (NSIDs) for which this repo contains at least one record.",
|
||||
"items": { "type": "string", "format": "nsid" }
|
||||
},
|
||||
"handleIsCorrect": {
|
||||
"type": "boolean",
|
||||
"description": "Indicates if handle is currently valid (resolves bi-directionally)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
at/lexicons/com/atproto/repo/getRecord.json
Normal file
45
at/lexicons/com/atproto/repo/getRecord.json
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "com.atproto.repo.getRecord",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "query",
|
||||
"description": "Get a single record from a repository. Does not require auth.",
|
||||
"parameters": {
|
||||
"type": "params",
|
||||
"required": ["repo", "collection", "rkey"],
|
||||
"properties": {
|
||||
"repo": {
|
||||
"type": "string",
|
||||
"format": "at-identifier",
|
||||
"description": "The handle or DID of the repo."
|
||||
},
|
||||
"collection": {
|
||||
"type": "string",
|
||||
"format": "nsid",
|
||||
"description": "The NSID of the record collection."
|
||||
},
|
||||
"rkey": { "type": "string", "description": "The Record Key." },
|
||||
"cid": {
|
||||
"type": "string",
|
||||
"format": "cid",
|
||||
"description": "The CID of the version of the record. If not specified, then return the most recent version."
|
||||
}
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["uri", "value"],
|
||||
"properties": {
|
||||
"uri": { "type": "string", "format": "at-uri" },
|
||||
"cid": { "type": "string", "format": "cid" },
|
||||
"value": { "type": "unknown" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": [{ "name": "RecordNotFound" }]
|
||||
}
|
||||
}
|
||||
}
|
13
at/lexicons/com/atproto/repo/importRepo.json
Normal file
13
at/lexicons/com/atproto/repo/importRepo.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "com.atproto.repo.importRepo",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "procedure",
|
||||
"description": "Import a repo in the form of a CAR file. Requires Content-Length HTTP header to be set.",
|
||||
"input": {
|
||||
"encoding": "application/vnd.ipld.car"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
44
at/lexicons/com/atproto/repo/listMissingBlobs.json
Normal file
44
at/lexicons/com/atproto/repo/listMissingBlobs.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "com.atproto.repo.listMissingBlobs",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "query",
|
||||
"description": "Returns a list of missing blobs for the requesting account. Intended to be used in the account migration flow.",
|
||||
"parameters": {
|
||||
"type": "params",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 1000,
|
||||
"default": 500
|
||||
},
|
||||
"cursor": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["blobs"],
|
||||
"properties": {
|
||||
"cursor": { "type": "string" },
|
||||
"blobs": {
|
||||
"type": "array",
|
||||
"items": { "type": "ref", "ref": "#recordBlob" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"recordBlob": {
|
||||
"type": "object",
|
||||
"required": ["cid", "recordUri"],
|
||||
"properties": {
|
||||
"cid": { "type": "string", "format": "cid" },
|
||||
"recordUri": { "type": "string", "format": "at-uri" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
69
at/lexicons/com/atproto/repo/listRecords.json
Normal file
69
at/lexicons/com/atproto/repo/listRecords.json
Normal file
@ -0,0 +1,69 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "com.atproto.repo.listRecords",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "query",
|
||||
"description": "List a range of records in a repository, matching a specific collection. Does not require auth.",
|
||||
"parameters": {
|
||||
"type": "params",
|
||||
"required": ["repo", "collection"],
|
||||
"properties": {
|
||||
"repo": {
|
||||
"type": "string",
|
||||
"format": "at-identifier",
|
||||
"description": "The handle or DID of the repo."
|
||||
},
|
||||
"collection": {
|
||||
"type": "string",
|
||||
"format": "nsid",
|
||||
"description": "The NSID of the record type."
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 100,
|
||||
"default": 50,
|
||||
"description": "The number of records to return."
|
||||
},
|
||||
"cursor": { "type": "string" },
|
||||
"rkeyStart": {
|
||||
"type": "string",
|
||||
"description": "DEPRECATED: The lowest sort-ordered rkey to start from (exclusive)"
|
||||
},
|
||||
"rkeyEnd": {
|
||||
"type": "string",
|
||||
"description": "DEPRECATED: The highest sort-ordered rkey to stop at (exclusive)"
|
||||
},
|
||||
"reverse": {
|
||||
"type": "boolean",
|
||||
"description": "Flag to reverse the order of the returned records."
|
||||
}
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["records"],
|
||||
"properties": {
|
||||
"cursor": { "type": "string" },
|
||||
"records": {
|
||||
"type": "array",
|
||||
"items": { "type": "ref", "ref": "#record" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"record": {
|
||||
"type": "object",
|
||||
"required": ["uri", "cid", "value"],
|
||||
"properties": {
|
||||
"uri": { "type": "string", "format": "at-uri" },
|
||||
"cid": { "type": "string", "format": "cid" },
|
||||
"value": { "type": "unknown" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
73
at/lexicons/com/atproto/repo/putRecord.json
Normal file
73
at/lexicons/com/atproto/repo/putRecord.json
Normal file
@ -0,0 +1,73 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "com.atproto.repo.putRecord",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "procedure",
|
||||
"description": "Write a repository record, creating or updating it as needed. Requires auth, implemented by PDS.",
|
||||
"input": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["repo", "collection", "rkey", "record"],
|
||||
"nullable": ["swapRecord"],
|
||||
"properties": {
|
||||
"repo": {
|
||||
"type": "string",
|
||||
"format": "at-identifier",
|
||||
"description": "The handle or DID of the repo (aka, current account)."
|
||||
},
|
||||
"collection": {
|
||||
"type": "string",
|
||||
"format": "nsid",
|
||||
"description": "The NSID of the record collection."
|
||||
},
|
||||
"rkey": {
|
||||
"type": "string",
|
||||
"description": "The Record Key.",
|
||||
"maxLength": 15
|
||||
},
|
||||
"validate": {
|
||||
"type": "boolean",
|
||||
"description": "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons."
|
||||
},
|
||||
"record": {
|
||||
"type": "unknown",
|
||||
"description": "The record to write."
|
||||
},
|
||||
"swapRecord": {
|
||||
"type": "string",
|
||||
"format": "cid",
|
||||
"description": "Compare and swap with the previous record by CID. WARNING: nullable and optional field; may cause problems with golang implementation"
|
||||
},
|
||||
"swapCommit": {
|
||||
"type": "string",
|
||||
"format": "cid",
|
||||
"description": "Compare and swap with the previous commit by CID."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["uri", "cid"],
|
||||
"properties": {
|
||||
"uri": { "type": "string", "format": "at-uri" },
|
||||
"cid": { "type": "string", "format": "cid" },
|
||||
"commit": {
|
||||
"type": "ref",
|
||||
"ref": "com.atproto.repo.defs#commitMeta"
|
||||
},
|
||||
"validationStatus": {
|
||||
"type": "string",
|
||||
"knownValues": ["valid", "unknown"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": [{ "name": "InvalidSwap" }]
|
||||
}
|
||||
}
|
||||
}
|
15
at/lexicons/com/atproto/repo/strongRef.json
Normal file
15
at/lexicons/com/atproto/repo/strongRef.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "com.atproto.repo.strongRef",
|
||||
"description": "A URI with a content-hash fingerprint.",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "object",
|
||||
"required": ["uri", "cid"],
|
||||
"properties": {
|
||||
"uri": { "type": "string", "format": "at-uri" },
|
||||
"cid": { "type": "string", "format": "cid" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
at/lexicons/com/atproto/repo/uploadBlob.json
Normal file
23
at/lexicons/com/atproto/repo/uploadBlob.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"lexicon": 1,
|
||||
"id": "com.atproto.repo.uploadBlob",
|
||||
"defs": {
|
||||
"main": {
|
||||
"type": "procedure",
|
||||
"description": "Upload a new blob, to be referenced from a repository record. The blob will be deleted if it is not referenced within a time window (eg, minutes). Blob restrictions (mimetype, size, etc) are enforced when the reference is created. Requires auth, implemented by PDS.",
|
||||
"input": {
|
||||
"encoding": "*/*"
|
||||
},
|
||||
"output": {
|
||||
"encoding": "application/json",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["blob"],
|
||||
"properties": {
|
||||
"blob": { "type": "blob" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
117
docs/README.md
117
docs/README.md
@ -1,117 +0,0 @@
|
||||
# ai.bot ドキュメント
|
||||
|
||||
ai.botプロジェクトの包括的なドキュメント集です。
|
||||
|
||||
## ドキュメント一覧
|
||||
|
||||
### 開発者向け
|
||||
|
||||
1. **[開発ガイド](./development-guide.md)**
|
||||
- プロジェクト概要とアーキテクチャ
|
||||
- 開発環境のセットアップ
|
||||
- 開発ワークフローとベストプラクティス
|
||||
- 新機能追加の手順
|
||||
|
||||
2. **[HTTPクライアントAPI](./http-client-api.md)**
|
||||
- HttpClientモジュールの完全なAPIリファレンス
|
||||
- 使用例とサンプルコード
|
||||
- エラーハンドリングのベストプラクティス
|
||||
|
||||
### 保守・運用向け
|
||||
|
||||
3. **[移行ガイド](./migration-guide.md)**
|
||||
- パッケージ名・CLI名の変更詳細
|
||||
- 段階的移行手順
|
||||
- 後方互換性の説明
|
||||
- トラブルシューティング
|
||||
|
||||
4. **[リファクタリングサマリー](./refactoring-summary.md)**
|
||||
- HTTPクライアント共通化の詳細
|
||||
- エラーハンドリング改善の記録
|
||||
- 対象ファイル一覧とBefore/After
|
||||
|
||||
## クイックスタート
|
||||
|
||||
### 1. 基本セットアップ
|
||||
```bash
|
||||
# 依存関係インストール
|
||||
cargo install cargo-make
|
||||
|
||||
# プロジェクトビルド
|
||||
cargo build
|
||||
|
||||
# テスト実行
|
||||
cargo test
|
||||
```
|
||||
|
||||
### 2. CLI使用方法
|
||||
```bash
|
||||
# 新しいコマンド(推奨)
|
||||
./target/debug/aibot --help
|
||||
|
||||
# 旧コマンド(互換性)
|
||||
./target/debug/ai --help
|
||||
```
|
||||
|
||||
### 3. 設定ディレクトリ
|
||||
- **新**: `~/.config/syui/ai/bot/`
|
||||
- **旧**: `~/.config/ai/` (自動移行対応)
|
||||
|
||||
## 主要な変更履歴
|
||||
|
||||
### 2025年6月6日 - 大規模リファクタリング
|
||||
|
||||
#### HTTPクライアント共通化
|
||||
- 24個のファイルをHttpClientモジュールで統合
|
||||
- コード重複を大幅削減
|
||||
- エラーハンドリングを改善(`.unwrap()` → `match`)
|
||||
|
||||
#### 命名規則統一
|
||||
- パッケージ名: `ai` → `aibot`
|
||||
- CLI名: `ai` → `aibot`(`ai`は互換性維持)
|
||||
- 設定ディレクトリ: `~/.config/ai/` → `~/.config/syui/ai/bot/`
|
||||
|
||||
#### テストインフラ構築
|
||||
- ユニットテストの追加
|
||||
- cargo-makeによるタスク管理
|
||||
- CI/CD対応の準備
|
||||
|
||||
## アーキテクチャ概要
|
||||
|
||||
```
|
||||
ai.bot/
|
||||
├── src/
|
||||
│ ├── main.rs # メインCLI (aibot)
|
||||
│ ├── alias.rs # 互換性CLI (ai)
|
||||
│ ├── http_client.rs # 統合HTTPクライアント
|
||||
│ ├── data.rs # 設定・データ管理
|
||||
│ ├── game/ # ゲーム機能
|
||||
│ └── tests/ # テストスイート
|
||||
├── docs/ # ドキュメント
|
||||
├── scripts/ # セットアップスクリプト
|
||||
└── ~/.config/syui/ai/bot/ # 設定ディレクトリ
|
||||
├── scpt/ # コマンドスクリプト
|
||||
└── txt/ # ログファイル
|
||||
```
|
||||
|
||||
## サポート・問い合わせ
|
||||
|
||||
### 開発関連
|
||||
- 新機能追加: [開発ガイド](./development-guide.md)を参照
|
||||
- API使用方法: [HTTPクライアントAPI](./http-client-api.md)を参照
|
||||
|
||||
### 移行・運用関連
|
||||
- 移行作業: [移行ガイド](./migration-guide.md)を参照
|
||||
- トラブル: 各ドキュメントのトラブルシューティング章を参照
|
||||
|
||||
### 履歴・詳細
|
||||
- 実装詳細: [リファクタリングサマリー](./refactoring-summary.md)を参照
|
||||
|
||||
## 貢献ガイドライン
|
||||
|
||||
1. **コードスタイル**: `cargo fmt`でフォーマット必須
|
||||
2. **テスト**: 新機能には対応するテストを追加
|
||||
3. **エラーハンドリング**: `.unwrap()`の使用禁止
|
||||
4. **ドキュメント**: 重要な変更は対応ドキュメントも更新
|
||||
|
||||
詳細は[開発ガイド](./development-guide.md)の「コントリビューション」章を参照してください。
|
@ -1,332 +0,0 @@
|
||||
# ai.bot 開発ガイド
|
||||
|
||||
## プロジェクト概要
|
||||
|
||||
ai.botは、Rust製のBluesky(AT Protocol)ボットです。メンション応答、コマンド実行、OpenAI統合などの機能を提供します。
|
||||
|
||||
**重要**: 2025年6月6日より、パッケージ名とCLI名が統一されました:
|
||||
- **新CLI名**: `aibot` (推奨)
|
||||
- **旧CLI名**: `ai` (互換性維持)
|
||||
- **設定ディレクトリ**: `~/.config/syui/ai/bot/` (旧: `~/.config/ai/`)
|
||||
|
||||
詳細は[移行ガイド](./migration-guide.md)を参照してください。
|
||||
|
||||
## アーキテクチャ
|
||||
|
||||
### 主要コンポーネント
|
||||
|
||||
1. **HTTPクライアント** (`src/http_client.rs`)
|
||||
- AT Protocol API呼び出しの統一インターフェース
|
||||
- 認証処理の自動化
|
||||
- エラーハンドリングの標準化
|
||||
|
||||
2. **コマンドシステム** (`src/main.rs`)
|
||||
- Seahorseを使用したCLIインターフェース
|
||||
- 各機能への振り分け
|
||||
|
||||
3. **AT Protocolモジュール**
|
||||
- 投稿、フォロー、いいね等の基本機能
|
||||
- 認証・セッション管理
|
||||
- フィード・通知処理
|
||||
|
||||
4. **ゲームシステム** (`src/game/`)
|
||||
- カードゲーム機能
|
||||
- ユーザー管理
|
||||
- ゲームデータ処理
|
||||
|
||||
5. **外部連携**
|
||||
- OpenAI API統合 (`src/openai.rs`)
|
||||
- 画像処理機能
|
||||
|
||||
## 開発環境セットアップ
|
||||
|
||||
### 必要なツール
|
||||
```bash
|
||||
# Rust(最新安定版)
|
||||
rustup update stable
|
||||
|
||||
# Cargo make(タスクランナー)
|
||||
cargo install cargo-make
|
||||
|
||||
# 開発用依存関係は自動インストール
|
||||
```
|
||||
|
||||
**注意**: 初回は`cargo install cargo-make`でcargo-makeのインストールが必要です。
|
||||
|
||||
### 設定ファイル
|
||||
```
|
||||
~/.config/syui/ai/bot/config.toml # 基本設定
|
||||
~/.config/syui/ai/bot/refresh # リフレッシュトークン
|
||||
~/.config/syui/ai/bot/access # アクセストークン
|
||||
```
|
||||
|
||||
**注意**: 旧設定ディレクトリ(`~/.config/ai/`)も自動的に参照・移行されます。
|
||||
|
||||
## 開発ワークフロー
|
||||
|
||||
### 1. コード変更
|
||||
```bash
|
||||
# フォーマット
|
||||
cargo fmt
|
||||
|
||||
# コンパイル確認
|
||||
cargo check
|
||||
|
||||
# テスト実行
|
||||
cargo test
|
||||
```
|
||||
|
||||
### 2. 統合ワークフロー
|
||||
```bash
|
||||
# 全体フロー(フォーマット→ビルド→テスト)
|
||||
cargo make my-flow
|
||||
```
|
||||
|
||||
### 3. 個別テスト
|
||||
```bash
|
||||
cargo make test-quick # 素早いテスト
|
||||
cargo make test-verbose # 詳細出力テスト
|
||||
```
|
||||
|
||||
## API呼び出しパターン
|
||||
|
||||
### HttpClientの使用方法
|
||||
|
||||
#### 基本的なGETリクエスト
|
||||
```rust
|
||||
use crate::http_client::HttpClient;
|
||||
|
||||
pub async fn get_user_profile(handle: String) -> String {
|
||||
let client = HttpClient::new();
|
||||
let url = format!("https://bsky.social/xrpc/app.bsky.actor.getProfile?actor={}", handle);
|
||||
|
||||
match client.get_with_auth(&url).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error getting profile: {}", e);
|
||||
"err".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### JSONを送信するPOSTリクエスト
|
||||
```rust
|
||||
use crate::http_client::HttpClient;
|
||||
use serde_json::json;
|
||||
|
||||
pub async fn create_post(text: String) -> String {
|
||||
let client = HttpClient::new();
|
||||
let url = "https://bsky.social/xrpc/com.atproto.repo.createRecord";
|
||||
|
||||
let payload = json!({
|
||||
"repo": "user.handle",
|
||||
"collection": "app.bsky.feed.post",
|
||||
"record": {
|
||||
"text": text,
|
||||
"createdAt": chrono::Utc::now().to_rfc3339()
|
||||
}
|
||||
});
|
||||
|
||||
match client.post_json_with_auth(&url, &payload).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error creating post: {}", e);
|
||||
"err".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## テストの書き方
|
||||
|
||||
### ユニットテスト
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_http_client_creation() {
|
||||
let client = HttpClient::new();
|
||||
// テストロジック
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_parsing() {
|
||||
// 同期テスト
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 統合テスト
|
||||
```rust
|
||||
#[tokio::test]
|
||||
#[ignore] // 通常は無視、環境変数設定時のみ実行
|
||||
async fn test_real_api() {
|
||||
if std::env::var("RUN_INTEGRATION_TESTS").is_ok() {
|
||||
// 実際のAPI呼び出しテスト
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## エラーハンドリングガイドライン
|
||||
|
||||
### 推奨パターン
|
||||
```rust
|
||||
// Good: 適切なエラーハンドリング
|
||||
match api_call().await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error in operation: {}", e);
|
||||
"err".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// Bad: unwrapの使用
|
||||
api_call().await.unwrap()
|
||||
```
|
||||
|
||||
### エラーレスポンス
|
||||
- API呼び出し失敗時は`"err"`文字列を返す
|
||||
- ログにエラー詳細を出力
|
||||
- 上位レイヤーでエラー処理を継続
|
||||
|
||||
## 新機能追加の手順
|
||||
|
||||
### 1. AT Protocol関連機能
|
||||
```bash
|
||||
# 1. モジュールファイルを作成
|
||||
touch src/new_feature.rs
|
||||
|
||||
# 2. main.rsに追加
|
||||
# pub mod new_feature;
|
||||
|
||||
# 3. HttpClientを使用して実装
|
||||
# 4. テストを追加
|
||||
# 5. main.rsのコマンドに追加
|
||||
```
|
||||
|
||||
### 2. 基本的なテンプレート
|
||||
```rust
|
||||
use crate::http_client::HttpClient;
|
||||
use crate::{data_toml, url};
|
||||
use serde_json::json;
|
||||
|
||||
pub async fn new_feature_request(param: String) -> String {
|
||||
let client = HttpClient::new();
|
||||
let endpoint_url = url(&"endpoint_name");
|
||||
|
||||
let payload = json!({
|
||||
"param": param
|
||||
});
|
||||
|
||||
match client.post_json_with_auth(&endpoint_url, &payload).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error in new_feature: {}", e);
|
||||
"err".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_new_feature() {
|
||||
// テスト実装
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## デバッグ方法
|
||||
|
||||
### 1. ログ出力
|
||||
```rust
|
||||
eprintln!("Debug: {}", variable);
|
||||
println!("Info: {}", message);
|
||||
```
|
||||
|
||||
### 2. テスト実行
|
||||
```bash
|
||||
# 特定のテストのみ
|
||||
cargo test test_name
|
||||
|
||||
# 詳細出力
|
||||
cargo test -- --nocapture
|
||||
|
||||
# 特定モジュール
|
||||
cargo test module_name
|
||||
```
|
||||
|
||||
### 3. HTTPリクエストのデバッグ
|
||||
HttpClientモジュール内でリクエスト/レスポンスをログ出力することで、API呼び出しの詳細を確認できます。
|
||||
|
||||
## パフォーマンス考慮事項
|
||||
|
||||
### HttpClientの再利用
|
||||
```rust
|
||||
// Good: 一度作成して再利用
|
||||
let client = HttpClient::new();
|
||||
for item in items {
|
||||
client.get_with_auth(&url).await;
|
||||
}
|
||||
|
||||
// Bad: 毎回新規作成
|
||||
for item in items {
|
||||
let client = HttpClient::new();
|
||||
client.get_with_auth(&url).await;
|
||||
}
|
||||
```
|
||||
|
||||
### 非同期処理
|
||||
- 可能な限り並列処理を活用
|
||||
- await呼び出しを最小限に
|
||||
- tokio::joinやfutures::join_allの活用
|
||||
|
||||
## トラブルシューティング
|
||||
|
||||
### よくある問題
|
||||
|
||||
1. **認証エラー**
|
||||
- トークンの有効期限切れ
|
||||
- 設定ファイルの不備
|
||||
|
||||
2. **コンパイルエラー**
|
||||
- 型不整合(特にライフタイム)
|
||||
- 未使用のimport
|
||||
|
||||
3. **実行時エラー**
|
||||
- ネットワーク接続問題
|
||||
- API仕様変更
|
||||
|
||||
### 解決方法
|
||||
```bash
|
||||
# 設定確認(新ディレクトリ)
|
||||
ls -la ~/.config/syui/ai/bot/
|
||||
# 旧ディレクトリも確認
|
||||
ls -la ~/.config/ai/
|
||||
|
||||
# トークンリフレッシュ
|
||||
./target/debug/aibot refresh
|
||||
# または互換性コマンド
|
||||
./target/debug/ai refresh
|
||||
|
||||
# 詳細ログ
|
||||
RUST_LOG=debug ./target/debug/aibot [command]
|
||||
```
|
||||
|
||||
## コントリビューション
|
||||
|
||||
1. コードフォーマット必須: `cargo fmt`
|
||||
2. テスト追加必須: 新機能には対応テスト
|
||||
3. エラーハンドリング必須: `.unwrap()`の使用禁止
|
||||
4. ドキュメント更新: 重要な変更は本ドキュメントも更新
|
||||
|
||||
## 関連ドキュメント
|
||||
|
||||
- [リファクタリングサマリー](./refactoring-summary.md)
|
||||
- [AT Protocol仕様](https://atproto.com/)
|
||||
- [Rust公式ドキュメント](https://doc.rust-lang.org/)
|
@ -1,334 +0,0 @@
|
||||
# HttpClient API リファレンス
|
||||
|
||||
## 概要
|
||||
|
||||
`HttpClient`は、AT Protocol APIへの統一されたHTTPクライアントインターフェースです。認証、エラーハンドリング、リクエスト管理を自動化します。
|
||||
|
||||
## 基本的な使用方法
|
||||
|
||||
```rust
|
||||
use crate::http_client::HttpClient;
|
||||
|
||||
let client = HttpClient::new();
|
||||
// または
|
||||
let client = HttpClient::default();
|
||||
```
|
||||
|
||||
## APIメソッド
|
||||
|
||||
### 認証付きリクエスト
|
||||
|
||||
#### get_with_auth
|
||||
AT Protocolの認証が必要なGETリクエストを実行します。
|
||||
|
||||
```rust
|
||||
pub async fn get_with_auth(&self, url: &str) -> Result<String, Error>
|
||||
```
|
||||
|
||||
**パラメータ:**
|
||||
- `url`: リクエスト先のURL
|
||||
|
||||
**戻り値:**
|
||||
- `Ok(String)`: レスポンスボディ(文字列)
|
||||
- `Err(Error)`: リクエストエラー
|
||||
|
||||
**使用例:**
|
||||
```rust
|
||||
let client = HttpClient::new();
|
||||
let url = "https://bsky.social/xrpc/app.bsky.actor.getProfile?actor=user.bsky.social";
|
||||
|
||||
match client.get_with_auth(&url).await {
|
||||
Ok(response) => println!("Response: {}", response),
|
||||
Err(e) => eprintln!("Error: {}", e),
|
||||
}
|
||||
```
|
||||
|
||||
#### post_json_with_auth
|
||||
AT Protocolの認証が必要なPOSTリクエスト(JSON)を実行します。
|
||||
|
||||
```rust
|
||||
pub async fn post_json_with_auth<T: Serialize>(&self, url: &str, json: &T) -> Result<String, Error>
|
||||
```
|
||||
|
||||
**パラメータ:**
|
||||
- `url`: リクエスト先のURL
|
||||
- `json`: シリアライズ可能なJSONデータ
|
||||
|
||||
**戻り値:**
|
||||
- `Ok(String)`: レスポンスボディ(文字列)
|
||||
- `Err(Error)`: リクエストエラー
|
||||
|
||||
**使用例:**
|
||||
```rust
|
||||
use serde_json::json;
|
||||
|
||||
let client = HttpClient::new();
|
||||
let url = "https://bsky.social/xrpc/com.atproto.repo.createRecord";
|
||||
let payload = json!({
|
||||
"repo": "user.bsky.social",
|
||||
"collection": "app.bsky.feed.post",
|
||||
"record": {
|
||||
"text": "Hello, World!",
|
||||
"createdAt": "2025-01-01T00:00:00Z"
|
||||
}
|
||||
});
|
||||
|
||||
match client.post_json_with_auth(&url, &payload).await {
|
||||
Ok(response) => println!("Post created: {}", response),
|
||||
Err(e) => eprintln!("Error: {}", e),
|
||||
}
|
||||
```
|
||||
|
||||
#### delete_with_auth
|
||||
AT Protocolの認証が必要なDELETEリクエストを実行します。
|
||||
|
||||
```rust
|
||||
pub async fn delete_with_auth(&self, url: &str) -> Result<String, Error>
|
||||
```
|
||||
|
||||
**パラメータ:**
|
||||
- `url`: リクエスト先のURL
|
||||
|
||||
**戻り値:**
|
||||
- `Ok(String)`: レスポンスボディ(文字列)
|
||||
- `Err(Error)`: リクエストエラー
|
||||
|
||||
**使用例:**
|
||||
```rust
|
||||
let client = HttpClient::new();
|
||||
let url = "https://bsky.social/xrpc/com.atproto.repo.deleteRecord";
|
||||
|
||||
match client.delete_with_auth(&url).await {
|
||||
Ok(response) => println!("Deleted: {}", response),
|
||||
Err(e) => eprintln!("Error: {}", e),
|
||||
}
|
||||
```
|
||||
|
||||
### 認証なしリクエスト
|
||||
|
||||
#### get
|
||||
認証なしのGETリクエストを実行します。
|
||||
|
||||
```rust
|
||||
pub async fn get(&self, url: &str) -> Result<String, Error>
|
||||
```
|
||||
|
||||
**使用例:**
|
||||
```rust
|
||||
let client = HttpClient::new();
|
||||
let url = "https://public-api.example.com/data";
|
||||
|
||||
match client.get(&url).await {
|
||||
Ok(response) => println!("Response: {}", response),
|
||||
Err(e) => eprintln!("Error: {}", e),
|
||||
}
|
||||
```
|
||||
|
||||
#### post_json
|
||||
認証なしのPOSTリクエスト(JSON)を実行します。ログイン処理などで使用。
|
||||
|
||||
```rust
|
||||
pub async fn post_json<T: Serialize>(&self, url: &str, json: &T) -> Result<String, Error>
|
||||
```
|
||||
|
||||
**使用例:**
|
||||
```rust
|
||||
use serde_json::json;
|
||||
|
||||
let client = HttpClient::new();
|
||||
let url = "https://bsky.social/xrpc/com.atproto.session.create";
|
||||
let credentials = json!({
|
||||
"identifier": "user.bsky.social",
|
||||
"password": "password"
|
||||
});
|
||||
|
||||
match client.post_json(&url, &credentials).await {
|
||||
Ok(response) => println!("Login successful: {}", response),
|
||||
Err(e) => eprintln!("Login failed: {}", e),
|
||||
}
|
||||
```
|
||||
|
||||
### カスタムヘッダー付きリクエスト
|
||||
|
||||
#### post_with_headers
|
||||
カスタムヘッダーを指定したPOSTリクエストを実行します。
|
||||
|
||||
```rust
|
||||
pub async fn post_with_headers<T: Serialize>(
|
||||
&self,
|
||||
url: &str,
|
||||
json: &T,
|
||||
headers: Vec<(&str, &str)>
|
||||
) -> Result<String, Error>
|
||||
```
|
||||
|
||||
**パラメータ:**
|
||||
- `url`: リクエスト先のURL
|
||||
- `json`: シリアライズ可能なJSONデータ
|
||||
- `headers`: ヘッダーのキーと値のペアのベクター
|
||||
|
||||
**使用例:**
|
||||
```rust
|
||||
use serde_json::json;
|
||||
|
||||
let client = HttpClient::new();
|
||||
let url = "https://bsky.social/xrpc/com.atproto.session.refresh";
|
||||
let refresh_token = "refresh_token_value";
|
||||
let auth_header = format!("Bearer {}", refresh_token);
|
||||
let headers = vec![("Authorization", auth_header.as_str())];
|
||||
let empty_json = json!({});
|
||||
|
||||
match client.post_with_headers(&url, &empty_json, headers).await {
|
||||
Ok(response) => println!("Token refreshed: {}", response),
|
||||
Err(e) => eprintln!("Refresh failed: {}", e),
|
||||
}
|
||||
```
|
||||
|
||||
## 内部実装詳細
|
||||
|
||||
### 認証処理
|
||||
認証付きメソッドは自動的に以下を実行します:
|
||||
|
||||
1. `data_refresh(&"access")`でアクセストークンを取得
|
||||
2. `Authorization: Bearer {token}`ヘッダーを自動追加
|
||||
3. リクエストを実行
|
||||
|
||||
### エラーハンドリング
|
||||
- ネットワークエラー
|
||||
- HTTPステータスエラー
|
||||
- JSON解析エラー
|
||||
- タイムアウトエラー
|
||||
|
||||
すべて`reqwest::Error`として統一されて返されます。
|
||||
|
||||
## 使用上の注意
|
||||
|
||||
### 1. トークン管理
|
||||
- アクセストークンは自動的に取得されます
|
||||
- トークンの有効期限切れは呼び出し元で処理する必要があります
|
||||
- リフレッシュトークンは`post_with_headers`で手動設定
|
||||
|
||||
### 2. エラー処理パターン
|
||||
```rust
|
||||
// 推奨パターン
|
||||
match client.get_with_auth(&url).await {
|
||||
Ok(response) => {
|
||||
// 成功処理
|
||||
response
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("API call failed: {}", e);
|
||||
"err".to_string() // 既存コードとの互換性
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. パフォーマンス
|
||||
- `HttpClient`の作成は軽量な操作です
|
||||
- 内部でrequwestクライアントを再利用しています
|
||||
- 複数のリクエストで同じインスタンスを使い回すことも可能
|
||||
|
||||
### 4. デバッグ
|
||||
リクエスト/レスポンスの詳細をログ出力したい場合:
|
||||
```rust
|
||||
// HttpClientモジュール内で適宜printlnを追加
|
||||
println!("Request URL: {}", url);
|
||||
println!("Response: {}", response);
|
||||
```
|
||||
|
||||
## 実装例:新しいAPI呼び出しモジュール
|
||||
|
||||
```rust
|
||||
use crate::http_client::HttpClient;
|
||||
use crate::{data_toml, url};
|
||||
use serde_json::json;
|
||||
use iso8601_timestamp::Timestamp;
|
||||
|
||||
pub async fn create_custom_record(data: String) -> String {
|
||||
let did = data_toml(&"did");
|
||||
let handle = data_toml(&"handle");
|
||||
let url = url(&"record_create");
|
||||
|
||||
let timestamp = Timestamp::now_utc().to_string();
|
||||
|
||||
let payload = json!({
|
||||
"repo": handle,
|
||||
"did": did,
|
||||
"collection": "app.bsky.custom.record",
|
||||
"record": {
|
||||
"data": data,
|
||||
"createdAt": timestamp
|
||||
}
|
||||
});
|
||||
|
||||
let client = HttpClient::new();
|
||||
match client.post_json_with_auth(&url, &payload).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error creating custom record: {}", e);
|
||||
"err".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_custom_record() {
|
||||
let result = create_custom_record("test data".to_string()).await;
|
||||
assert_ne!(result, "err");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 移行ガイド
|
||||
|
||||
### 既存コードからの移行
|
||||
|
||||
#### Before (旧実装)
|
||||
```rust
|
||||
extern crate reqwest;
|
||||
use crate::data_refresh;
|
||||
|
||||
pub async fn old_request() -> String {
|
||||
let token = data_refresh(&"access");
|
||||
let client = reqwest::Client::new();
|
||||
let res = client
|
||||
.post(url)
|
||||
.json(&data)
|
||||
.header("Authorization", "Bearer ".to_owned() + &token)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
res
|
||||
}
|
||||
```
|
||||
|
||||
#### After (新実装)
|
||||
```rust
|
||||
use crate::http_client::HttpClient;
|
||||
|
||||
pub async fn new_request() -> String {
|
||||
let client = HttpClient::new();
|
||||
match client.post_json_with_auth(&url, &data).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error in request: {}", e);
|
||||
"err".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### チェックリスト
|
||||
- [ ] `extern crate reqwest;`を削除
|
||||
- [ ] `use crate::http_client::HttpClient;`を追加
|
||||
- [ ] `data_refresh(&"access")`の手動呼び出しを削除
|
||||
- [ ] `reqwest::Client::new()`を`HttpClient::new()`に置換
|
||||
- [ ] `.unwrap()`を適切な`match`文に置換
|
||||
- [ ] エラーメッセージの追加
|
@ -1,271 +0,0 @@
|
||||
# ai.bot 段階的移行ガイド
|
||||
|
||||
## 概要
|
||||
|
||||
ai.botプロジェクトの命名規則とディレクトリ構造を統一するための段階的移行作業の記録です。
|
||||
|
||||
## 移行内容
|
||||
|
||||
### 1. パッケージ・CLI名の変更
|
||||
|
||||
| 項目 | 変更前 | 変更後 |
|
||||
|------|--------|--------|
|
||||
| パッケージ名 | `ai` | `aibot` |
|
||||
| メインCLI | `ai` | `aibot` |
|
||||
| 互換性CLI | - | `ai` (aibotへのエイリアス) |
|
||||
|
||||
### 2. ディレクトリ構造の変更
|
||||
|
||||
| 用途 | 変更前 | 変更後 |
|
||||
|------|--------|--------|
|
||||
| 設定ディレクトリ | `~/.config/ai/` | `~/.config/syui/ai/bot/` |
|
||||
| ログディレクトリ | `~/.config/ai/txt/` | `~/.config/syui/ai/bot/txt/` |
|
||||
| スクリプトディレクトリ | `~/.config/ai/scpt/` | `~/.config/syui/ai/bot/scpt/` |
|
||||
|
||||
## 実装した機能
|
||||
|
||||
### 1. デュアルバイナリシステム
|
||||
|
||||
#### Cargo.toml設定
|
||||
```toml
|
||||
[package]
|
||||
name = "aibot"
|
||||
description = "ai.bot - Bluesky AT Protocol Bot"
|
||||
|
||||
[[bin]]
|
||||
name = "aibot"
|
||||
path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "ai"
|
||||
path = "src/alias.rs"
|
||||
```
|
||||
|
||||
#### エイリアスバイナリ (src/alias.rs)
|
||||
- `ai`コマンドが`aibot`を自動的に呼び出し
|
||||
- 同一ディレクトリ内のaibotバイナリを優先検索
|
||||
- PATH内のaibotもフォールバック対応
|
||||
|
||||
### 2. 設定ディレクトリの自動移行
|
||||
|
||||
#### data_file関数の改良
|
||||
```rust
|
||||
pub fn data_file(s: &str) -> String {
|
||||
// 新しい設定ディレクトリ(優先)
|
||||
let new_config_dir = "/.config/syui/ai/bot/";
|
||||
// 旧設定ディレクトリ(互換性のため)
|
||||
let old_config_dir = "/.config/ai/";
|
||||
|
||||
// 自動移行ロジック
|
||||
// 1. 新しいパスにファイルが存在 → 新しいパスを使用
|
||||
// 2. 旧パスのみに存在 → 新しいパスにコピーして使用
|
||||
// 3. どちらにも存在しない → 新しいパスを使用
|
||||
}
|
||||
```
|
||||
|
||||
#### log_file関数も同様の移行対応
|
||||
|
||||
### 3. 移行セットアップスクリプト
|
||||
|
||||
#### scripts/setup-migration.sh
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 新しい設定ディレクトリの作成
|
||||
mkdir -p ~/.config/syui/ai/bot/
|
||||
|
||||
# スクリプトディレクトリのコピー
|
||||
cp -r ~/.config/ai/scpt ~/.config/syui/ai/bot/
|
||||
|
||||
# エイリアス設定の提案
|
||||
echo "alias ai='aibot'"
|
||||
```
|
||||
|
||||
## 後方互換性
|
||||
|
||||
### 1. 設定ファイル
|
||||
- **自動移行**: 旧パスから新パスへ自動コピー
|
||||
- **継続読み込み**: 移行後も旧パスは参照可能
|
||||
- **透過的**: ユーザーは変更を意識する必要なし
|
||||
|
||||
### 2. コマンドライン
|
||||
- **aiコマンド**: 既存スクリプトで引き続き使用可能
|
||||
- **aibotコマンド**: 新しい正式名称
|
||||
- **完全互換**: 引数、オプション、出力すべて同一
|
||||
|
||||
### 3. スクリプト
|
||||
- **shellscript**: `ai`コマンドをそのまま使用可能
|
||||
- **エイリアス推奨**: `alias ai='aibot'`で統一
|
||||
|
||||
## 移行手順
|
||||
|
||||
### 1. 自動移行(推奨)
|
||||
|
||||
```bash
|
||||
# プロジェクトをビルド
|
||||
cargo build
|
||||
|
||||
# 移行スクリプトを実行
|
||||
./scripts/setup-migration.sh
|
||||
|
||||
# 新しいバイナリを使用
|
||||
./target/debug/aibot --help
|
||||
./target/debug/ai --help # 互換性確認
|
||||
```
|
||||
|
||||
### 2. 手動移行
|
||||
|
||||
```bash
|
||||
# 1. 新しい設定ディレクトリ作成
|
||||
mkdir -p ~/.config/syui/ai/bot/
|
||||
|
||||
# 2. スクリプトディレクトリのコピー
|
||||
cp -r ~/.config/ai/scpt ~/.config/syui/ai/bot/
|
||||
|
||||
# 3. バイナリのインストール
|
||||
cargo install --path .
|
||||
|
||||
# 4. エイリアス設定(オプション)
|
||||
echo "alias ai='aibot'" >> ~/.zshrc
|
||||
```
|
||||
|
||||
### 3. 段階的移行(企業環境等)
|
||||
|
||||
```bash
|
||||
# Phase 1: 新しいバイナリの導入
|
||||
cargo install --path . --bin aibot
|
||||
|
||||
# Phase 2: エイリアス設定
|
||||
echo "alias ai='aibot'" >> ~/.profile
|
||||
|
||||
# Phase 3: スクリプトの段階的更新
|
||||
# (既存スクリプトは変更不要)
|
||||
|
||||
# Phase 4: 旧設定の完全移行(任意のタイミング)
|
||||
rm -rf ~/.config/ai/ # 十分な検証後
|
||||
```
|
||||
|
||||
## 影響範囲
|
||||
|
||||
### 1. 変更が必要な箇所
|
||||
- ❌ **なし** (完全後方互換)
|
||||
|
||||
### 2. 変更が推奨される箇所
|
||||
- 📝 shellrcでのエイリアス設定
|
||||
- 📝 ドキュメント内のコマンド例
|
||||
- 📝 CI/CDスクリプト(新しい名前使用)
|
||||
|
||||
### 3. 変更が不要な箇所
|
||||
- ✅ 既存のshellscript
|
||||
- ✅ 設定ファイル
|
||||
- ✅ ログファイル
|
||||
- ✅ git submodule
|
||||
|
||||
## トラブルシューティング
|
||||
|
||||
### 1. 設定ファイルが見つからない
|
||||
|
||||
```bash
|
||||
# 現在の設定ディレクトリを確認
|
||||
ls -la ~/.config/syui/ai/bot/
|
||||
ls -la ~/.config/ai/ # 旧ディレクトリ
|
||||
|
||||
# 手動でコピー
|
||||
cp ~/.config/ai/token.json ~/.config/syui/ai/bot/
|
||||
```
|
||||
|
||||
### 2. aiコマンドが動作しない
|
||||
|
||||
```bash
|
||||
# aibotバイナリの存在確認
|
||||
which aibot
|
||||
./target/debug/aibot --help
|
||||
|
||||
# エイリアスの設定
|
||||
alias ai='aibot'
|
||||
# または
|
||||
export PATH="$(pwd)/target/debug:$PATH"
|
||||
```
|
||||
|
||||
### 3. スクリプトディレクトリが見つからない
|
||||
|
||||
```bash
|
||||
# 手動でコピー
|
||||
cp -r ~/.config/ai/scpt ~/.config/syui/ai/bot/
|
||||
|
||||
# gitサブモジュールの場合
|
||||
cd ~/.config/syui/ai/bot/scpt
|
||||
git remote -v # リモートURLを確認
|
||||
```
|
||||
|
||||
## 検証方法
|
||||
|
||||
### 1. 基本動作確認
|
||||
|
||||
```bash
|
||||
# 新しいコマンド
|
||||
aibot --help
|
||||
aibot post "Test from aibot"
|
||||
|
||||
# 互換性確認
|
||||
ai --help
|
||||
ai post "Test from ai alias"
|
||||
|
||||
# 設定ディレクトリ確認
|
||||
ls -la ~/.config/syui/ai/bot/
|
||||
```
|
||||
|
||||
### 2. 自動移行確認
|
||||
|
||||
```bash
|
||||
# 旧設定で動作確認
|
||||
touch ~/.config/ai/test_file
|
||||
aibot some_command # 新ディレクトリにコピーされることを確認
|
||||
ls -la ~/.config/syui/ai/bot/test_file
|
||||
```
|
||||
|
||||
### 3. スクリプト互換性確認
|
||||
|
||||
```bash
|
||||
# 既存スクリプトの動作確認
|
||||
cd ~/.config/syui/ai/bot/scpt
|
||||
./ai.zsh # aiコマンドが正常動作することを確認
|
||||
```
|
||||
|
||||
## 今後の予定
|
||||
|
||||
### Phase 1 (完了)
|
||||
- ✅ パッケージ名の変更
|
||||
- ✅ デュアルバイナリシステム
|
||||
- ✅ 設定ディレクトリの自動移行
|
||||
- ✅ 後方互換性の確保
|
||||
|
||||
### Phase 2 (将来)
|
||||
- 📅 ドキュメントの更新
|
||||
- 📅 CI/CDの新コマンド対応
|
||||
- 📅 パフォーマンス最適化
|
||||
|
||||
### Phase 3 (任意)
|
||||
- 📅 旧設定ディレクトリの削除
|
||||
- 📅 エイリアスバイナリの削除
|
||||
- 📅 完全な新体系への移行
|
||||
|
||||
## 関連ファイル
|
||||
|
||||
- `Cargo.toml` - パッケージ設定
|
||||
- `src/alias.rs` - エイリアスバイナリ
|
||||
- `src/data.rs` - 設定ディレクトリ管理
|
||||
- `scripts/setup-migration.sh` - 移行スクリプト
|
||||
- `docs/migration-guide.md` - 本ドキュメント
|
||||
|
||||
## 注意事項
|
||||
|
||||
1. **gitサブモジュール**: scptディレクトリがgitサブモジュールの場合、リモートURLの確認が必要
|
||||
2. **権限**: 設定ディレクトリの権限は適切に設定すること
|
||||
3. **バックアップ**: 重要な設定は移行前にバックアップを推奨
|
||||
4. **テスト**: 本番環境での使用前に十分なテストを実施
|
||||
|
||||
## 参考情報
|
||||
|
||||
- [開発ガイド](./development-guide.md)
|
||||
- [リファクタリングサマリー](./refactoring-summary.md)
|
||||
- [HTTPクライアントAPI](./http-client-api.md)
|
@ -1,208 +0,0 @@
|
||||
# ai.bot リファクタリング作業サマリー
|
||||
|
||||
## 概要
|
||||
2025年6月6日に実施されたai.botプロジェクトのリファクタリング作業の完全な記録です。
|
||||
|
||||
## 実施した作業
|
||||
|
||||
### 1. HTTPクライアントの共通化
|
||||
- **作成したファイル**: `src/http_client.rs`
|
||||
- **対象**: 24個のファイルを統合
|
||||
- **削減**: 重複するHTTPリクエストコードを一箇所に集約
|
||||
|
||||
#### HttpClientモジュールの機能
|
||||
```rust
|
||||
// 認証付きリクエスト
|
||||
client.get_with_auth(&url).await
|
||||
client.post_json_with_auth(&url, &json_data).await
|
||||
client.delete_with_auth(&url).await
|
||||
|
||||
// 認証なしリクエスト
|
||||
client.get(&url).await
|
||||
client.post_json(&url, &json_data).await
|
||||
|
||||
// カスタムヘッダー付きリクエスト
|
||||
client.post_with_headers(&url, &json_data, headers).await
|
||||
```
|
||||
|
||||
### 2. リファクタリング対象ファイル一覧
|
||||
|
||||
#### コアAPIファイル (8個)
|
||||
- `src/post.rs` - 投稿作成
|
||||
- `src/like.rs` - いいね機能
|
||||
- `src/repost.rs` - リポスト機能
|
||||
- `src/follow.rs` - フォロー/アンフォロー
|
||||
- `src/reply.rs` - 返信機能
|
||||
- `src/profile.rs` - プロフィール取得
|
||||
- `src/delete_record.rs` - レコード削除
|
||||
- `src/describe.rs` - ユーザー説明取得
|
||||
|
||||
#### 認証・セッション管理 (3個)
|
||||
- `src/session.rs` - セッション管理
|
||||
- `src/refresh.rs` - トークンリフレッシュ
|
||||
- `src/token.rs` - ログイン認証
|
||||
|
||||
#### 通知・メンション (3個)
|
||||
- `src/mention.rs` - メンション投稿
|
||||
- `src/notify.rs` - 通知取得
|
||||
- `src/notify_read.rs` - 通知既読
|
||||
|
||||
#### フィード・タイムライン (4個)
|
||||
- `src/feed_get.rs` - フィード取得
|
||||
- `src/timeline_author.rs` - 作者タイムライン
|
||||
- `src/followers.rs` - フォロワー取得
|
||||
- `src/follows.rs` - フォロー取得
|
||||
|
||||
#### 画像関連 (3個)
|
||||
- `src/img.rs` - 画像投稿
|
||||
- `src/img_reply.rs` - 画像付き返信
|
||||
- `src/img_upload.rs` - 画像アップロード
|
||||
|
||||
#### リンク・リッチテキスト (3個)
|
||||
- `src/post_link.rs` - リンク付き投稿
|
||||
- `src/reply_link.rs` - リンク付き返信
|
||||
- `src/reply_og.rs` - OGデータ付き返信
|
||||
|
||||
#### ゲームモジュール (5個)
|
||||
- `src/game/post_card.rs` - ゲームカード投稿
|
||||
- `src/game/post_card_verify.rs` - カード検証
|
||||
- `src/game/post_game.rs` - ゲームデータ投稿
|
||||
- `src/game/post_game_login.rs` - ゲームログイン
|
||||
- `src/game/post_game_user.rs` - ゲームユーザーデータ
|
||||
|
||||
### 3. 除外したファイル
|
||||
- `src/openai.rs` - OpenAI API用(異なる認証方式のため)
|
||||
- `src/feed_watch.rs` - reqwest使用していないため
|
||||
|
||||
### 4. 共通の変更パターン
|
||||
|
||||
#### Before (変更前)
|
||||
```rust
|
||||
extern crate reqwest;
|
||||
use crate::data_refresh;
|
||||
|
||||
pub async fn some_request() -> String {
|
||||
let token = data_refresh(&"access");
|
||||
let client = reqwest::Client::new();
|
||||
let res = client
|
||||
.post(url)
|
||||
.json(&post)
|
||||
.header("Authorization", "Bearer ".to_owned() + &token)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
res
|
||||
}
|
||||
```
|
||||
|
||||
#### After (変更後)
|
||||
```rust
|
||||
use crate::http_client::HttpClient;
|
||||
|
||||
pub async fn some_request() -> String {
|
||||
let client = HttpClient::new();
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error: {}", e);
|
||||
"err".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. テストインフラの追加
|
||||
|
||||
#### 作成したテストファイル
|
||||
- `src/tests/mod.rs` - テストモジュール宣言
|
||||
- `src/tests/http_client_tests.rs` - HttpClientのテスト
|
||||
|
||||
#### テストコマンド
|
||||
```bash
|
||||
# 基本テスト
|
||||
cargo test
|
||||
|
||||
# Makefileを使用したテスト
|
||||
cargo make test # cleanしてからテスト
|
||||
cargo make test-quick # 素早いテスト
|
||||
cargo make test-verbose # 詳細出力
|
||||
```
|
||||
|
||||
#### 追加した依存関係 (Cargo.toml)
|
||||
```toml
|
||||
[dev-dependencies]
|
||||
mockito = "1.2"
|
||||
tokio-test = "0.4"
|
||||
```
|
||||
|
||||
## 改善された点
|
||||
|
||||
### コード品質
|
||||
- **コード重複の削除**: 同じHTTPリクエストパターンの重複を排除
|
||||
- **エラーハンドリング**: `.unwrap()`を適切な`match`文に置換
|
||||
- **保守性**: HTTP関連のロジックが一箇所に集約
|
||||
|
||||
### 開発効率
|
||||
- **変更容易性**: HTTPクライアントの変更が1ファイルで完結
|
||||
- **テスト可能性**: ユニットテストの追加でバグ検出が容易
|
||||
- **デバッグ性**: エラーメッセージの改善
|
||||
|
||||
### 安定性
|
||||
- **パニック回避**: `.unwrap()`によるパニックを防止
|
||||
- **エラー処理**: 適切なエラーレスポンスの返却
|
||||
|
||||
## 次のステップ(推奨)
|
||||
|
||||
### 1. bot.rsのリファクタリング (高優先度)
|
||||
- 500行以上の巨大な関数を分割
|
||||
- コマンド処理の構造化
|
||||
- 深いif-else文の改善
|
||||
|
||||
### 2. 設定管理の統一 (中優先度)
|
||||
- 複数の設定構造体の統合
|
||||
- 設定ファイルパスの一元管理
|
||||
|
||||
### 3. main.rsの整理 (中優先度)
|
||||
- コマンド定義の外部ファイル化
|
||||
- モジュール構造の改善
|
||||
|
||||
## ファイル構造
|
||||
|
||||
```
|
||||
src/
|
||||
├── http_client.rs # 新規作成:共通HTTPクライアント
|
||||
├── tests/ # 新規作成:テストディレクトリ
|
||||
│ ├── mod.rs
|
||||
│ └── http_client_tests.rs
|
||||
├── main.rs # 更新:http_clientモジュール追加
|
||||
├── Cargo.toml # 更新:テスト依存関係追加
|
||||
├── Makefile.toml # 更新:テストコマンド追加
|
||||
└── [24個のリファクタリング済みファイル]
|
||||
```
|
||||
|
||||
## 注意事項
|
||||
|
||||
1. **OpenAI API**: `src/openai.rs`は意図的に除外(異なる認証方式)
|
||||
2. **後方互換性**: 既存のAPI呼び出しは同じ形式を維持
|
||||
3. **エラー処理**: "err"文字列を返すパターンは既存仕様に合わせて維持
|
||||
|
||||
## 検証方法
|
||||
|
||||
```bash
|
||||
# コンパイル確認
|
||||
cargo check
|
||||
|
||||
# テスト実行
|
||||
cargo test
|
||||
|
||||
# フォーマット確認
|
||||
cargo fmt --check
|
||||
|
||||
# 全体ビルド
|
||||
cargo build
|
||||
```
|
||||
|
||||
この作業により、ai.botプロジェクトのHTTP通信部分が大幅に改善され、今後の開発・保守が容易になりました。
|
@ -1,37 +0,0 @@
|
||||
#!/bin/bash
|
||||
# ai.bot 移行セットアップスクリプト
|
||||
|
||||
echo "=== ai.bot Migration Setup ==="
|
||||
|
||||
# 1. 新しい設定ディレクトリの作成
|
||||
echo "Creating new config directory..."
|
||||
mkdir -p ~/.config/syui/ai/bot/
|
||||
|
||||
# 2. スクリプトディレクトリの移動(gitサブモジュール)
|
||||
if [ -d ~/.config/ai/scpt ]; then
|
||||
echo "Copying script directory..."
|
||||
cp -r ~/.config/ai/scpt ~/.config/syui/ai/bot/
|
||||
echo "Scripts copied to ~/.config/syui/ai/bot/scpt/"
|
||||
fi
|
||||
|
||||
# 3. 設定ファイルの移行(自動的にdata.rsで行われる)
|
||||
echo "Configuration files will be migrated automatically when used."
|
||||
|
||||
# 4. エイリアス設定の提案
|
||||
echo ""
|
||||
echo "=== Manual Steps Required ==="
|
||||
echo ""
|
||||
echo "1. Add this alias to your shell profile (~/.zshrc, ~/.bashrc, etc.):"
|
||||
echo " alias ai='aibot'"
|
||||
echo ""
|
||||
echo "2. Install the new binaries:"
|
||||
echo " cargo install --path ."
|
||||
echo ""
|
||||
echo "3. Or add to PATH:"
|
||||
echo " export PATH=\"$(pwd)/target/debug:\$PATH\""
|
||||
echo ""
|
||||
echo "4. Update git submodule path if needed:"
|
||||
echo " cd ~/.config/syui/ai/bot/scpt"
|
||||
echo " git remote -v # Check current remote"
|
||||
echo ""
|
||||
echo "Migration setup complete!"
|
39
src/alias.rs
39
src/alias.rs
@ -1,39 +0,0 @@
|
||||
// Legacy alias: ai -> aibot
|
||||
// This provides backward compatibility for existing scripts
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, exit};
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
// Skip the first argument (program name) and pass the rest to aibot
|
||||
let aibot_args: Vec<&str> = args.iter().skip(1).map(|s| s.as_str()).collect();
|
||||
|
||||
// Try to find aibot binary in the same directory as this binary
|
||||
let current_exe = env::current_exe().unwrap_or_else(|_| PathBuf::from("ai"));
|
||||
let mut aibot_path = current_exe.parent().unwrap_or(&PathBuf::from(".")).to_path_buf();
|
||||
aibot_path.push("aibot");
|
||||
|
||||
// First try relative path, then try PATH
|
||||
let mut cmd = if aibot_path.exists() {
|
||||
Command::new(&aibot_path)
|
||||
} else {
|
||||
Command::new("aibot")
|
||||
};
|
||||
|
||||
cmd.args(&aibot_args);
|
||||
|
||||
match cmd.status() {
|
||||
Ok(status) => {
|
||||
exit(status.code().unwrap_or(1));
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error executing aibot: {}", e);
|
||||
eprintln!("Make sure 'aibot' is installed and available in PATH");
|
||||
eprintln!("Attempted path: {:?}", aibot_path);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
36
src/bot.rs
36
src/bot.rs
@ -431,42 +431,6 @@ pub fn c_bot(c: &Context) {
|
||||
println!("{}", str_rep);
|
||||
w_cid(cid.to_string(), log_file(&"n1"), true);
|
||||
}
|
||||
} else if com == "game" || com == "/game" {
|
||||
let output = Command::new(data_scpt(&"ai"))
|
||||
.arg(&"atproto").arg(&"game")
|
||||
.arg(&handle)
|
||||
.arg(&did)
|
||||
.arg(&cid)
|
||||
.arg(&uri)
|
||||
.arg(&cid_root)
|
||||
.arg(&uri_root)
|
||||
.arg(&host)
|
||||
.arg(&prompt)
|
||||
.arg(&prompt_sub)
|
||||
.output()
|
||||
.expect("zsh");
|
||||
let d = String::from_utf8_lossy(&output.stdout);
|
||||
let dd = "\n".to_owned() + &d.to_string();
|
||||
let text_limit = c_char(dd);
|
||||
handlev = d.lines().collect::<Vec<_>>()[0].to_string();
|
||||
link = "https://card.syui.ai/".to_owned() + &handlev;
|
||||
println!("{}", e);
|
||||
e = link.chars().count();
|
||||
if text_limit.len() > 3 {
|
||||
let str_rep = reply_link::post_request(
|
||||
text_limit.to_string(),
|
||||
link.to_string(),
|
||||
s,
|
||||
e.try_into().unwrap(),
|
||||
cid.to_string(),
|
||||
uri.to_string(),
|
||||
cid_root.to_string(),
|
||||
uri_root.to_string(),
|
||||
)
|
||||
.await;
|
||||
println!("{}", str_rep);
|
||||
w_cid(cid.to_string(), log_file(&"n1"), true);
|
||||
}
|
||||
} else if com == "quiz" || com == "/quiz" {
|
||||
println!("admin:{}", admin);
|
||||
let output = Command::new(data_scpt(&"ai"))
|
||||
|
103
src/data.rs
103
src/data.rs
@ -8,91 +8,35 @@ use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn data_file(s: &str) -> String {
|
||||
// 新しい設定ディレクトリ(優先)
|
||||
let new_config_dir = "/.config/syui/ai/bot/";
|
||||
let mut new_path = shellexpand::tilde("~").to_string();
|
||||
new_path.push_str(&new_config_dir);
|
||||
|
||||
// 旧設定ディレクトリ(互換性のため)
|
||||
let old_config_dir = "/.config/ai/";
|
||||
let mut old_path = shellexpand::tilde("~").to_string();
|
||||
old_path.push_str(&old_config_dir);
|
||||
|
||||
// 新しいディレクトリを作成
|
||||
let new_dir = Path::new(&new_path);
|
||||
if !new_dir.is_dir() {
|
||||
let _ = fs::create_dir_all(new_path.clone());
|
||||
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());
|
||||
}
|
||||
|
||||
let filename = match &*s {
|
||||
"toml" => "token.toml",
|
||||
"json" => "token.json",
|
||||
"refresh" => "refresh.toml",
|
||||
_ => &format!(".{}", s),
|
||||
};
|
||||
|
||||
let new_file = new_path.clone() + filename;
|
||||
let old_file = old_path + filename;
|
||||
|
||||
// 新しいパスにファイルが存在する場合は新しいパスを使用
|
||||
if Path::new(&new_file).exists() {
|
||||
return new_file;
|
||||
match &*s {
|
||||
"toml" => f + &"token.toml",
|
||||
"json" => f + &"token.json",
|
||||
"refresh" => f + &"refresh.toml",
|
||||
_ => f + &"." + &s,
|
||||
}
|
||||
|
||||
// 旧パスにファイルが存在し、新しいパスに存在しない場合は移行を試行
|
||||
if Path::new(&old_file).exists() && !Path::new(&new_file).exists() {
|
||||
if let Ok(_) = fs::copy(&old_file, &new_file) {
|
||||
eprintln!("Migrated config file: {} -> {}", old_file, new_file);
|
||||
return new_file;
|
||||
}
|
||||
}
|
||||
|
||||
// デフォルトは新しいパス
|
||||
new_file
|
||||
}
|
||||
|
||||
pub fn log_file(s: &str) -> String {
|
||||
// 新しい設定ディレクトリ(優先)
|
||||
let new_log_dir = "/.config/syui/ai/bot/txt/";
|
||||
let mut new_path = shellexpand::tilde("~").to_string();
|
||||
new_path.push_str(&new_log_dir);
|
||||
|
||||
// 旧設定ディレクトリ(互換性のため)
|
||||
let old_log_dir = "/.config/ai/txt/";
|
||||
let mut old_path = shellexpand::tilde("~").to_string();
|
||||
old_path.push_str(&old_log_dir);
|
||||
|
||||
// 新しいディレクトリを作成
|
||||
let new_dir = Path::new(&new_path);
|
||||
if !new_dir.is_dir() {
|
||||
let _ = fs::create_dir_all(new_path.clone());
|
||||
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());
|
||||
}
|
||||
|
||||
let filename = match &*s {
|
||||
"n1" => "notify_cid.txt",
|
||||
"n2" => "notify_cid_run.txt",
|
||||
"c1" => "comment_cid.txt",
|
||||
_ => s,
|
||||
};
|
||||
|
||||
let new_file = new_path.clone() + filename;
|
||||
let old_file = old_path + filename;
|
||||
|
||||
// 新しいパスにファイルが存在する場合は新しいパスを使用
|
||||
if Path::new(&new_file).exists() {
|
||||
return new_file;
|
||||
match &*s {
|
||||
"n1" => f + &"notify_cid.txt",
|
||||
"n2" => f + &"notify_cid_run.txt",
|
||||
"c1" => f + &"comment_cid.txt",
|
||||
_ => f + &s,
|
||||
}
|
||||
|
||||
// 旧パスにファイルが存在し、新しいパスに存在しない場合は移行を試行
|
||||
if Path::new(&old_file).exists() && !Path::new(&new_file).exists() {
|
||||
if let Ok(_) = fs::copy(&old_file, &new_file) {
|
||||
eprintln!("Migrated log file: {} -> {}", old_file, new_file);
|
||||
return new_file;
|
||||
}
|
||||
}
|
||||
|
||||
// デフォルトは新しいパス
|
||||
new_file
|
||||
}
|
||||
|
||||
impl Token {
|
||||
@ -163,7 +107,6 @@ 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,
|
||||
@ -199,7 +142,6 @@ 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(),
|
||||
@ -231,7 +173,6 @@ 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,
|
||||
|
@ -1,21 +1,33 @@
|
||||
use crate::http_client::HttpClient;
|
||||
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 client = HttpClient::new();
|
||||
|
||||
let post = json!({
|
||||
let url = url(&"record_delete");
|
||||
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"rkey": rkey.to_string(),
|
||||
"collection": col.to_string()
|
||||
});
|
||||
}));
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -1,13 +1,22 @@
|
||||
use crate::http_client::HttpClient;
|
||||
extern crate reqwest;
|
||||
//use crate::data_toml;
|
||||
use crate::url;
|
||||
|
||||
pub async fn get_request(user: String) -> String {
|
||||
let base_url = url(&"describe");
|
||||
let url = format!("{}?repo={}", base_url, user);
|
||||
let client = HttpClient::new();
|
||||
//let token = data_refresh(&"access");
|
||||
let url = url(&"describe");
|
||||
|
||||
match client.get(&url).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -1,13 +1,33 @@
|
||||
use crate::http_client::HttpClient;
|
||||
extern crate reqwest;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
|
||||
pub async fn get_request(feed: String) -> String {
|
||||
let base_url = url(&"feed_get");
|
||||
let url = format!("{}?feed={}", base_url, feed);
|
||||
let client = HttpClient::new();
|
||||
let token = data_refresh(&"access");
|
||||
let url = url(&"feed_get");
|
||||
let feed = feed.to_string();
|
||||
//let col = "app.bsky.feed.generator".to_string();
|
||||
|
||||
match client.get_with_auth(&url).await {
|
||||
Ok(response) => response,
|
||||
Err(_) => "err".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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
extern crate reqwest;
|
||||
use crate::data_toml;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
use crate::http_client::HttpClient;
|
||||
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");
|
||||
|
||||
@ -16,7 +18,7 @@ pub async fn post_request(u: String) -> String {
|
||||
let d = Timestamp::now_utc();
|
||||
let d = d.to_string();
|
||||
|
||||
let post = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -24,19 +26,25 @@ pub async fn post_request(u: String) -> String {
|
||||
"subject": u.to_string(),
|
||||
"createdAt": d.to_string(),
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error following user: {}", e);
|
||||
"err".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");
|
||||
|
||||
@ -46,7 +54,7 @@ pub async fn delete_request(u: String, rkey: String) -> String {
|
||||
let d = Timestamp::now_utc();
|
||||
let d = d.to_string();
|
||||
|
||||
let post = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -55,14 +63,19 @@ pub async fn delete_request(u: String, rkey: String) -> String {
|
||||
"subject": u.to_string(),
|
||||
"createdAt": d.to_string(),
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error unfollowing user: {}", e);
|
||||
"err".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;
|
||||
}
|
||||
|
@ -1,14 +1,24 @@
|
||||
use crate::http_client::HttpClient;
|
||||
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 base_url = url(&"followers");
|
||||
let token = data_refresh(&"access");
|
||||
let url = url(&"followers");
|
||||
let cursor = cursor.unwrap();
|
||||
let url = format!("{}?actor={}&cursor={}", base_url, actor, cursor);
|
||||
let client = HttpClient::new();
|
||||
|
||||
match client.get_with_auth(&url).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -1,14 +1,26 @@
|
||||
use crate::http_client::HttpClient;
|
||||
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 base_url = url(&"follows");
|
||||
let token = data_refresh(&"access");
|
||||
let url = url(&"follows");
|
||||
let cursor = cursor.unwrap();
|
||||
let url = format!("{}?actor={}&cursor={}", base_url, actor, cursor);
|
||||
let client = HttpClient::new();
|
||||
//let cursor = "1682386039125::bafyreihwgwozmvqxcxrhbr65agcaa4v357p27ccrhzkjf3mz5xiozjvzfa".to_string();
|
||||
//let cursor = "1682385956974::bafyreihivhux5m3sxbg33yruhw5ozhahwspnuqdsysbo57smzgptdcluem".to_string();
|
||||
|
||||
match client.get_with_auth(&url).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
pub mod post_card;
|
||||
pub mod post_card_verify;
|
||||
pub mod post_game;
|
||||
pub mod post_game_user;
|
||||
pub mod post_game_login;
|
@ -1,30 +0,0 @@
|
||||
use crate::http_client::HttpClient;
|
||||
use crate::data_toml;
|
||||
use crate::url;
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use serde_json::json;
|
||||
|
||||
pub async fn post_request(col: String, account: String) -> String {
|
||||
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 = 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 = HttpClient::new();
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
use crate::http_client::HttpClient;
|
||||
use crate::data_toml;
|
||||
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 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 = 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 = HttpClient::new();
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
use crate::http_client::HttpClient;
|
||||
use crate::data_toml;
|
||||
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 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 = 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 = HttpClient::new();
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
use reqwest::{Client, Error};
|
||||
use serde::Serialize;
|
||||
use crate::data_refresh;
|
||||
|
||||
pub struct HttpClient {
|
||||
client: Client,
|
||||
}
|
||||
|
||||
impl HttpClient {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
client: Client::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// GET request with authentication
|
||||
pub async fn get_with_auth(&self, url: &str) -> Result<String, Error> {
|
||||
let token = data_refresh(&"access");
|
||||
|
||||
let response = self.client
|
||||
.get(url)
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// POST request with JSON body and authentication
|
||||
pub async fn post_json_with_auth<T: Serialize>(&self, url: &str, json: &T) -> Result<String, Error> {
|
||||
let token = data_refresh(&"access");
|
||||
|
||||
let response = self.client
|
||||
.post(url)
|
||||
.json(json)
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// DELETE request with authentication
|
||||
pub async fn delete_with_auth(&self, url: &str) -> Result<String, Error> {
|
||||
let token = data_refresh(&"access");
|
||||
|
||||
let response = self.client
|
||||
.delete(url)
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// POST request without authentication (for login, etc.)
|
||||
pub async fn post_json<T: Serialize>(&self, url: &str, json: &T) -> Result<String, Error> {
|
||||
let response = self.client
|
||||
.post(url)
|
||||
.json(json)
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// GET request without authentication
|
||||
pub async fn get(&self, url: &str) -> Result<String, Error> {
|
||||
let response = self.client
|
||||
.get(url)
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// POST request with custom headers
|
||||
pub async fn post_with_headers<T: Serialize>(
|
||||
&self,
|
||||
url: &str,
|
||||
json: &T,
|
||||
headers: Vec<(&str, &str)>
|
||||
) -> Result<String, Error> {
|
||||
let mut request = self.client.post(url).json(json);
|
||||
|
||||
for (key, value) in headers {
|
||||
request = request.header(key, value);
|
||||
}
|
||||
|
||||
let response = request
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HttpClient {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
29
src/img.rs
29
src/img.rs
@ -1,19 +1,23 @@
|
||||
use crate::http_client::HttpClient;
|
||||
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 = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -37,12 +41,19 @@ pub async fn post_request(text: String, link: String) -> String {
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::http_client::HttpClient;
|
||||
extern crate reqwest;
|
||||
use crate::data_toml;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use serde_json::json;
|
||||
@ -11,15 +12,17 @@ pub async fn post_request(
|
||||
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 = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -53,12 +56,19 @@ pub async fn post_request(
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -1,19 +1,23 @@
|
||||
use crate::http_client::HttpClient;
|
||||
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 = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -37,12 +41,19 @@ pub async fn post_request(text: String, link: String) -> String {
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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
|
||||
}
|
||||
|
29
src/like.rs
29
src/like.rs
@ -1,10 +1,12 @@
|
||||
extern crate reqwest;
|
||||
use crate::data_toml;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
use crate::http_client::HttpClient;
|
||||
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");
|
||||
|
||||
@ -14,7 +16,7 @@ pub async fn post_request(cid: String, uri: String) -> String {
|
||||
let d = Timestamp::now_utc();
|
||||
let d = d.to_string();
|
||||
|
||||
let post = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -25,14 +27,19 @@ pub async fn post_request(cid: String, uri: String) -> String {
|
||||
},
|
||||
"createdAt": d.to_string(),
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error liking post: {}", e);
|
||||
"err".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;
|
||||
}
|
||||
|
197
src/main.rs
197
src/main.rs
@ -12,11 +12,6 @@ use crate::data::url;
|
||||
use crate::data::w_cfg;
|
||||
use crate::data::w_refresh;
|
||||
use crate::feed_watch::c_feed_watch;
|
||||
use crate::game::post_card;
|
||||
use crate::game::post_card_verify;
|
||||
use crate::game::post_game;
|
||||
use crate::game::post_game_user;
|
||||
use crate::game::post_game_login;
|
||||
|
||||
use data::ProfileIdentityResolve;
|
||||
|
||||
@ -27,7 +22,6 @@ pub mod describe;
|
||||
pub mod follow;
|
||||
pub mod followers;
|
||||
pub mod follows;
|
||||
pub mod http_client;
|
||||
pub mod img_reply;
|
||||
pub mod like;
|
||||
pub mod mention;
|
||||
@ -36,7 +30,8 @@ pub mod notify_read;
|
||||
pub mod openai;
|
||||
pub mod post;
|
||||
pub mod post_link;
|
||||
pub mod game;
|
||||
pub mod post_card;
|
||||
pub mod post_card_verify;
|
||||
pub mod profile;
|
||||
pub mod refresh;
|
||||
pub mod reply;
|
||||
@ -50,9 +45,6 @@ pub mod feed_get;
|
||||
pub mod feed_watch;
|
||||
pub mod delete_record;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let app = App::new(env!("CARGO_PKG_NAME"))
|
||||
@ -201,7 +193,7 @@ fn main() {
|
||||
)
|
||||
.command(
|
||||
Command::new("card-verify")
|
||||
.description("<at://verify> -c <collection> -i <id> -p <cp> -r <rank> -rare <normal> -H <syui.ai> -d <did>")
|
||||
.description("<at://verify> -c <collection> -i <id> -p <cp> -r <rank> -rare <normal> -h <syui.ai> -d <did>")
|
||||
.action(card_verify)
|
||||
.flag(
|
||||
Flag::new("col", FlagType::String)
|
||||
@ -224,100 +216,13 @@ fn main() {
|
||||
)
|
||||
.flag(
|
||||
Flag::new("handle", FlagType::String)
|
||||
.alias("H"),
|
||||
.alias("handle"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new("did", FlagType::String)
|
||||
.alias("did"),
|
||||
)
|
||||
)
|
||||
.command(
|
||||
Command::new("game")
|
||||
.description("a <at://yui.syui.ai/ai.syui.game.user/username>")
|
||||
.action(game)
|
||||
.flag(
|
||||
Flag::new("col", FlagType::String)
|
||||
.alias("c"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new("account", FlagType::String)
|
||||
.alias("a"),
|
||||
)
|
||||
)
|
||||
.command(
|
||||
Command::new("game-login")
|
||||
.description("l <bool> -u <username> -c <collection>")
|
||||
.action(game_login)
|
||||
.flag(
|
||||
Flag::new("col", FlagType::String)
|
||||
.alias("c"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new("login", FlagType::Bool)
|
||||
.alias("l"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new("username", FlagType::String)
|
||||
.alias("u"),
|
||||
)
|
||||
)
|
||||
.command(
|
||||
Command::new("game-user")
|
||||
.description("-chara ai -l 20240101 -ten 0 --lv 0 --exp 0 --hp 0 --rank 0 --mode 0 --attach 0 --critical 0 --critical_d 0")
|
||||
.action(game_user)
|
||||
.flag(
|
||||
Flag::new("username", FlagType::String)
|
||||
.alias("u"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new("col", FlagType::String)
|
||||
.alias("c"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new("did", FlagType::String)
|
||||
.alias("d"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new("handle", FlagType::String)
|
||||
.alias("H"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new("character", FlagType::String)
|
||||
.alias("chara"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new("aiten", FlagType::Int)
|
||||
.alias("ten"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new("limit", FlagType::Int)
|
||||
.alias("l"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new("lv", FlagType::Int)
|
||||
)
|
||||
.flag(
|
||||
Flag::new("hp", FlagType::Int)
|
||||
)
|
||||
.flag(
|
||||
Flag::new("attach", FlagType::Int)
|
||||
)
|
||||
.flag(
|
||||
Flag::new("exp", FlagType::Int)
|
||||
)
|
||||
.flag(
|
||||
Flag::new("critical", FlagType::Int)
|
||||
)
|
||||
.flag(
|
||||
Flag::new("critical_d", FlagType::Int)
|
||||
)
|
||||
.flag(
|
||||
Flag::new("rank", FlagType::Int)
|
||||
)
|
||||
.flag(
|
||||
Flag::new("mode", FlagType::Int)
|
||||
)
|
||||
)
|
||||
.command(
|
||||
Command::new("like")
|
||||
.description("like <cid> -u <uri>")
|
||||
@ -398,10 +303,6 @@ fn main() {
|
||||
Flag::new("post", FlagType::String)
|
||||
.alias("p"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new("col", FlagType::String)
|
||||
.alias("c"),
|
||||
)
|
||||
)
|
||||
.command(
|
||||
Command::new("follow")
|
||||
@ -677,8 +578,6 @@ async fn c_card_verify(c: &Context) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let rare = c.string_flag("rare").unwrap_or_else(|_| "normal".to_string());
|
||||
let user_handle = c.string_flag("handle").unwrap_or_else(|_| "syui.ai".to_string());
|
||||
let user_did = c.string_flag("did").unwrap_or_else(|_| "did:plc:uqzpqmrjnptsxezjx4xuh2mn".to_string());
|
||||
|
||||
//match id === 1 let img = "xxx";
|
||||
let str = post_card_verify::post_request(col, img, id, cp, rank, rare, user_handle, user_did);
|
||||
println!("{}", str.await);
|
||||
Ok(())
|
||||
@ -695,92 +594,6 @@ fn card_verify(c: &Context) {
|
||||
});
|
||||
}
|
||||
|
||||
async fn c_game(c: &Context) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let account = c.string_flag("account").unwrap_or_else(|_| "at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.game.user/syui".to_string());
|
||||
let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.game".to_string());
|
||||
let handle = data_toml(&"handle");
|
||||
if handle == "syui.ai" {
|
||||
let str = post_game::post_request(col, account);
|
||||
println!("{}", str.await);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Box::new(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Not authorized")))
|
||||
}
|
||||
}
|
||||
|
||||
fn game(c: &Context) {
|
||||
refresh(c);
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
if let Err(e) = c_game(c).await {
|
||||
eprintln!("Error: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn c_game_user(c: &Context) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.game.user".to_string());
|
||||
let user_name = c.string_flag("username").unwrap_or_else(|_| "syui".to_string());
|
||||
let user_handle = c.string_flag("handle").unwrap_or_else(|_| "syui.ai".to_string());
|
||||
let user_did = c.string_flag("did").unwrap_or_else(|_| "did:plc:uqzpqmrjnptsxezjx4xuh2mn".to_string());
|
||||
let chara = c.string_flag("character").unwrap_or_else(|_| "ai".to_string());
|
||||
let limit = c.int_flag("limit")?.try_into()?;
|
||||
let aiten = c.int_flag("aiten")?.try_into()?;
|
||||
let lv = c.int_flag("lv")?.try_into()?;
|
||||
let exp = c.int_flag("exp")?.try_into()?;
|
||||
let hp = c.int_flag("hp")?.try_into()?;
|
||||
let rank = c.int_flag("rank")?.try_into()?;
|
||||
let mode = c.int_flag("mode")?.try_into()?;
|
||||
let attach = c.int_flag("attach")?.try_into()?;
|
||||
let critical = c.int_flag("critical")?.try_into()?;
|
||||
let critical_d = c.int_flag("critical_d")?.try_into()?;
|
||||
|
||||
if data_toml(&"handle") == "yui.syui.ai" {
|
||||
let str = post_game_user::post_request(col, user_name, user_did, user_handle, aiten, limit, chara, lv, exp, hp, rank, mode, attach, critical, critical_d);
|
||||
println!("{}", str.await);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Box::new(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Not authorized")))
|
||||
}
|
||||
}
|
||||
|
||||
fn game_user(c: &Context) {
|
||||
refresh(c);
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
if let Err(e) = c_game_user(c).await {
|
||||
eprintln!("Error: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn c_game_login(c: &Context) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.game.login".to_string());
|
||||
let user_name = c.string_flag("username").unwrap_or_else(|_| "syui".to_string());
|
||||
let account = "at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.game.user/".to_string() + &user_name;
|
||||
let login = c.bool_flag("login");
|
||||
if data_toml(&"handle") == "yui.syui.ai" {
|
||||
let str = post_game_login::post_request(col, user_name, login, account);
|
||||
println!("{}", str.await);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Box::new(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Not authorized")))
|
||||
}
|
||||
}
|
||||
|
||||
fn game_login(c: &Context) {
|
||||
refresh(c);
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
if let Err(e) = c_game_login(c).await {
|
||||
eprintln!("Error: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn repost(c: &Context) {
|
||||
refresh(c);
|
||||
let m = c.args[0].to_string();
|
||||
@ -835,7 +648,6 @@ fn mention(c: &Context) {
|
||||
let h = async {
|
||||
let str = profile::get_request(m.to_string()).await;
|
||||
let profile: ProfileIdentityResolve = serde_json::from_str(&str).unwrap();
|
||||
let col = c.string_flag("col").unwrap_or_else(|_| "app.bsky.feed.post".to_string());
|
||||
let udid = profile.did;
|
||||
let handle = m.to_string();
|
||||
let at = "@".to_owned() + &handle;
|
||||
@ -843,7 +655,6 @@ fn mention(c: &Context) {
|
||||
let s = 0;
|
||||
if let Ok(post) = c.string_flag("post") {
|
||||
let str = mention::post_request(
|
||||
col,
|
||||
post.to_string(),
|
||||
at.to_string(),
|
||||
udid.to_string(),
|
||||
|
@ -1,24 +1,28 @@
|
||||
use crate::http_client::HttpClient;
|
||||
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 {
|
||||
pub async fn post_request(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 = json!({
|
||||
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(),
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": d.to_string(),
|
||||
"facets": [
|
||||
{
|
||||
@ -35,12 +39,19 @@ pub async fn post_request(col: String, text: String, at: String, udid: String, s
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -1,13 +1,30 @@
|
||||
use crate::http_client::HttpClient;
|
||||
extern crate reqwest;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
//use serde_json::json;
|
||||
|
||||
pub async fn get_request(limit: i32) -> String {
|
||||
let base_url = url(&"notify_list");
|
||||
let url = format!("{}?limit={}", base_url, limit);
|
||||
let client = HttpClient::new();
|
||||
let token = data_refresh(&"access");
|
||||
let url = url(&"notify_list");
|
||||
|
||||
match client.get_with_auth(&url).await {
|
||||
Ok(response) => response,
|
||||
Err(_) => "err".to_string(),
|
||||
let client = reqwest::Client::new();
|
||||
let res = client
|
||||
.get(url)
|
||||
.query(&[("limit", limit)])
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,27 @@
|
||||
use crate::http_client::HttpClient;
|
||||
extern crate reqwest;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
use serde_json::json;
|
||||
|
||||
pub async fn post_request(time: String) -> String {
|
||||
let token = data_refresh(&"access");
|
||||
let url = url(&"notify_update");
|
||||
let client = HttpClient::new();
|
||||
|
||||
let post = json!({
|
||||
let post = Some(json!({
|
||||
"seenAt": time.to_string(),
|
||||
});
|
||||
}));
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
29
src/post.rs
29
src/post.rs
@ -1,10 +1,12 @@
|
||||
extern crate reqwest;
|
||||
use crate::data_toml;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
use crate::http_client::HttpClient;
|
||||
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");
|
||||
|
||||
@ -14,7 +16,7 @@ pub async fn post_request(text: String) -> String {
|
||||
let d = Timestamp::now_utc();
|
||||
let d = d.to_string();
|
||||
|
||||
let post = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -22,14 +24,19 @@ pub async fn post_request(text: String) -> String {
|
||||
"text": text.to_string(),
|
||||
"createdAt": d.to_string(),
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error posting: {}", e);
|
||||
"err".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;
|
||||
}
|
||||
|
@ -1,17 +1,21 @@
|
||||
use crate::http_client::HttpClient;
|
||||
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 = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -24,12 +28,19 @@ pub async fn post_request(verify: String, id: i32, cp: i32, rank: i32, rare: Str
|
||||
"verify": verify.to_string(),
|
||||
"createdAt": d.to_string(),
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
@ -1,17 +1,19 @@
|
||||
use crate::http_client::HttpClient;
|
||||
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 = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -38,12 +40,19 @@ pub async fn post_request(col: String, img: String, id: i32, cp: i32, rank: i32,
|
||||
},
|
||||
"createdAt": d.to_string(),
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
@ -1,19 +1,22 @@
|
||||
use crate::http_client::HttpClient;
|
||||
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 = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -35,12 +38,19 @@ pub async fn post_request(text: String, link: String, s: i32, e: i32) -> String
|
||||
}
|
||||
],
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -1,15 +1,21 @@
|
||||
extern crate reqwest;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
use crate::http_client::HttpClient;
|
||||
|
||||
pub async fn get_request(user: String) -> String {
|
||||
let token = data_refresh(&"access");
|
||||
let url = url(&"profile_get") + &"?handle=" + &user;
|
||||
|
||||
let client = HttpClient::new();
|
||||
match client.get_with_auth(&url).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error getting profile: {}", e);
|
||||
"err".to_string()
|
||||
}
|
||||
}
|
||||
let client = reqwest::Client::new();
|
||||
let res = client
|
||||
.get(url)
|
||||
.header("Authorization", "Bearer ".to_owned() + &token)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -1,18 +1,28 @@
|
||||
use crate::http_client::HttpClient;
|
||||
extern crate reqwest;
|
||||
use crate::data_toml;
|
||||
use crate::url;
|
||||
|
||||
pub async fn post_request() -> String {
|
||||
let refresh = data_toml(&"refresh");
|
||||
let url = url(&"session_refresh");
|
||||
let client = HttpClient::new();
|
||||
|
||||
let auth_header = format!("Bearer {}", refresh);
|
||||
let headers = vec![("Authorization", auth_header.as_str())];
|
||||
let empty_json = serde_json::json!({});
|
||||
|
||||
match client.post_with_headers(&url, &empty_json, headers).await {
|
||||
Ok(response) => response,
|
||||
Err(_) => "err".to_string(),
|
||||
let client = reqwest::Client::new();
|
||||
let res = client
|
||||
.post(url)
|
||||
.header("Authorization", "Bearer ".to_owned() + &refresh)
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
29
src/reply.rs
29
src/reply.rs
@ -1,6 +1,7 @@
|
||||
extern crate reqwest;
|
||||
use crate::data_toml;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
use crate::http_client::HttpClient;
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use serde_json::json;
|
||||
|
||||
@ -11,6 +12,7 @@ pub async fn post_request(
|
||||
cid_root: String,
|
||||
uri_root: String,
|
||||
) -> String {
|
||||
let token = data_refresh(&"access");
|
||||
let did = data_toml(&"did");
|
||||
let handle = data_toml(&"handle");
|
||||
|
||||
@ -21,7 +23,7 @@ pub async fn post_request(
|
||||
let d = Timestamp::now_utc();
|
||||
let d = d.to_string();
|
||||
|
||||
let post = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -39,14 +41,19 @@ pub async fn post_request(
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error replying to post: {}", e);
|
||||
"err".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;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::http_client::HttpClient;
|
||||
extern crate reqwest;
|
||||
use crate::data_toml;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use serde_json::json;
|
||||
@ -14,15 +15,17 @@ pub async fn post_request(
|
||||
cid_root: String,
|
||||
uri_root: 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 = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -54,12 +57,19 @@ pub async fn post_request(
|
||||
}
|
||||
],
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::http_client::HttpClient;
|
||||
extern crate reqwest;
|
||||
use crate::data_toml;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use serde_json::json;
|
||||
@ -15,15 +16,17 @@ pub async fn post_request(
|
||||
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 = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -57,12 +60,19 @@ pub async fn post_request(
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
extern crate reqwest;
|
||||
use crate::data_toml;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
use crate::http_client::HttpClient;
|
||||
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");
|
||||
|
||||
@ -14,7 +16,7 @@ pub async fn post_request(cid: String, uri: String) -> String {
|
||||
let d = Timestamp::now_utc();
|
||||
let d = d.to_string();
|
||||
|
||||
let post = json!({
|
||||
let post = Some(json!({
|
||||
"repo": handle.to_string(),
|
||||
"did": did.to_string(),
|
||||
"collection": col.to_string(),
|
||||
@ -25,14 +27,19 @@ pub async fn post_request(cid: String, uri: String) -> String {
|
||||
},
|
||||
"createdAt": d.to_string(),
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
let client = HttpClient::new();
|
||||
match client.post_json_with_auth(&url, &post).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
eprintln!("Error reposting: {}", e);
|
||||
"err".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;
|
||||
}
|
||||
|
@ -1,12 +1,28 @@
|
||||
use crate::http_client::HttpClient;
|
||||
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 = HttpClient::new();
|
||||
|
||||
match client.get_with_auth(&url).await {
|
||||
Ok(response) => response,
|
||||
Err(_) => "err".to_string(),
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
use crate::http_client::HttpClient;
|
||||
|
||||
#[test]
|
||||
fn test_http_client_creation() {
|
||||
let _client = HttpClient::new();
|
||||
// HttpClientが正しく作成されることを確認
|
||||
let _client2 = HttpClient::default();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_http_client_error_handling() {
|
||||
let client = HttpClient::new();
|
||||
|
||||
// 無効なURLでエラーが返ることを確認
|
||||
let result = client.get("http://invalid-url-that-does-not-exist.local").await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// モジュールが正しくコンパイルされることを確認
|
||||
#[test]
|
||||
fn test_module_imports() {
|
||||
// モジュールが存在することを確認
|
||||
assert!(true);
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
#[cfg(test)]
|
||||
mod http_client_tests;
|
@ -1,14 +1,27 @@
|
||||
use crate::http_client::HttpClient;
|
||||
extern crate reqwest;
|
||||
use crate::data_refresh;
|
||||
use crate::url;
|
||||
|
||||
pub async fn get_request(actor: String) -> String {
|
||||
let base_url = url(&"record_list");
|
||||
let col = "app.bsky.feed.post".to_string();
|
||||
let url = format!("{}?repo={}&collection={}", base_url, actor, col);
|
||||
let client = HttpClient::new();
|
||||
let token = data_refresh(&"access");
|
||||
let url = url(&"record_list");
|
||||
|
||||
match client.get_with_auth(&url).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
24
src/token.rs
24
src/token.rs
@ -1,17 +1,25 @@
|
||||
use crate::http_client::HttpClient;
|
||||
extern crate reqwest;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub async fn post_request(handle: String, pass: String, host: String) -> String {
|
||||
let url = format!("https://{}/xrpc/com.atproto.server.createSession", host);
|
||||
let url = "https://".to_owned()
|
||||
+ &host.to_string()
|
||||
+ &"/xrpc/com.atproto.server.createSession".to_string();
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert("identifier", &handle);
|
||||
map.insert("password", &pass);
|
||||
|
||||
let client = HttpClient::new();
|
||||
|
||||
match client.post_json(&url, &map).await {
|
||||
Ok(response) => response,
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
let client = reqwest::Client::new();
|
||||
let res = client
|
||||
.post(url)
|
||||
.json(&map)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
#!/bin/zsh
|
||||
|
||||
#ai l $HANDLE -p $PASSWORD -s $HOST
|
||||
ai bot -a $ADMIN
|
||||
ai l $HANDLE -p $PASSWORD -s $HOST && ai bot -a $ADMIN
|
||||
|
Reference in New Issue
Block a user