Compare commits
87 Commits
Author | SHA1 | Date | |
---|---|---|---|
c74fc25a2f | |||
a2773e2705 | |||
f1e37c9ff5 | |||
56bf8b0293 | |||
e2f616df73 | |||
2f94ed4305 | |||
21abc54ccd | |||
5b0f184ea6 | |||
8f5aed806c | |||
641a9cd40e | |||
c510019165 | |||
6bd734d50b | |||
b201e9d789 | |||
99af00d0d7 | |||
ffe384916d | |||
91c355ccde | |||
131329c95a | |||
bdb295b207 | |||
c3b7c9824d | |||
2e8a090f53 | |||
0337c40a05 | |||
b3b8527746 | |||
edbfc8dd62 | |||
51c51ea9ed | |||
af010ddd55 | |||
21b35d489b | |||
76022bc3e1 | |||
59a1248bf6 | |||
ec972bc5a1 | |||
4190a21ba2 | |||
835200e5e6 | |||
7565b29a37 | |||
b43467459b | |||
3823ba32b0 | |||
142b91685b | |||
7691f6f820 | |||
41d93d08ec | |||
4e7b20e593 | |||
ac50961dde | |||
a1b851fd3d | |||
ea3b0e1e86 | |||
45244d7188 | |||
a9da7f91c3 | |||
d3fcaf352d | |||
25ff6427ff | |||
d3825ce466 | |||
65a4a527eb | |||
9f4cf82142 | |||
637bf4d95b | |||
4ecd66ec34 | |||
58fb822d6f | |||
001b585bb0 | |||
71cac1fcda | |||
45883d6f21 | |||
caefc2a88f | |||
bfc6ea5939 | |||
86ab1be93c | |||
599bd71e7b | |||
9b21a9ebff | |||
dee1f090a7 | |||
28ec9d0751 | |||
4c26ef6485 | |||
fe3077cb55 | |||
3e62896e31 | |||
709ab3ca78 | |||
d207cd20b9 | |||
86b3fc8029 | |||
4c980beb90 | |||
9a4bf1c641 | |||
7d098d3595 | |||
b65b417db6 | |||
95814b7b7c | |||
b53a512d3a | |||
7a38f1513f | |||
a524395ec2 | |||
e223fb3dd4 | |||
eb90fb5909 | |||
1a11510354 | |||
8bb9c0ed0b | |||
00e5ed84b7 | |||
c0212858be | |||
3e8b7b063a | |||
46b8c3843f | |||
2b32524063 | |||
85702ae349 | |||
b23fa6a846 | |||
1726bd38cb |
49
README.md
49
README.md
|
@ -1,21 +1,14 @@
|
||||||
# gnuboard5-activitypub
|
# gnuboard5-activitypub
|
||||||
ActivityPub implementation for GNUBOARD 5
|
GNUBOARD5-ActivityPub: ActivityPub (Fediverse) implementation for GNUBOARD5
|
||||||
|
|
||||||
## References
|
* https://sir.kr/g5_plugin/10381
|
||||||
* https://www.w3.org/TR/activitypub/
|
* https://codeberg.org/fediverse/delightful-activitypub-development
|
||||||
* https://www.w3.org/TR/activitystreams-core/
|
|
||||||
* https://www.w3.org/TR/activitystreams-vocabulary/
|
|
||||||
* https://github.com/w3c/activitypub/issues/194
|
|
||||||
* https://docs.joinmastodon.org/spec/webfinger/
|
|
||||||
* https://organicdesign.nz/ActivityPub_Code
|
|
||||||
* https://socialhub.activitypub.rocks/t/posting-to-pleroma-inbox/1184
|
|
||||||
* https://github.com/broidHQ/integrations/tree/master/broid-schemas#readme
|
|
||||||
|
|
||||||
## 사용 전 설정
|
## 사용 전 설정
|
||||||
* `apstreams` 게시판 추가
|
* `apstreams` 게시판 추가
|
||||||
* `apstreams` 사용자 추가
|
* `apstreams` 사용자 추가
|
||||||
|
|
||||||
## 작업진행
|
## 지원현황
|
||||||
- [x] WebFinger
|
- [x] WebFinger
|
||||||
- [x] User
|
- [x] User
|
||||||
- [x] Inbox
|
- [x] Inbox
|
||||||
|
@ -23,14 +16,24 @@ ActivityPub implementation for GNUBOARD 5
|
||||||
- [x] Followers
|
- [x] Followers
|
||||||
- [x] Following
|
- [x] Following
|
||||||
- [x] Liked
|
- [x] Liked
|
||||||
- [x] (Added) Geolocation
|
- [ ] ~~Shares~~ (Altered to inbound/outbound)
|
||||||
- [ ] (Added) File attachment
|
- [x] Geolocation
|
||||||
|
- [x] File attachment
|
||||||
|
- [ ] File attachment - Automatically download a remote file to the local server
|
||||||
|
- [x] Digest/Signature - Outbound
|
||||||
|
- [ ] ~~Digest/Signature - Inbound~~ (No required)
|
||||||
|
- [x] w3id.org (e.g. the `publicKey` field of an actor)
|
||||||
|
- [ ] OAuth 2.0
|
||||||
|
- [ ] Message Queue Compatible (e.g. Redis, RebbitMQ, Kafka)
|
||||||
|
|
||||||
## 부가기능 (옵션)
|
## 부가기능 (옵션)
|
||||||
|
- [x] 아바타 (gravatar.com)
|
||||||
- [x] 날씨 (openweathermap.org)
|
- [x] 날씨 (openweathermap.org)
|
||||||
- [x] 환율 (koreaexim.go.kr)
|
- [x] 환율 (koreaexim.go.kr)
|
||||||
|
- [x] 국내 Geolocation (Naver Cloud)
|
||||||
|
- [x] 국외 Geolocation (IP2Location)
|
||||||
|
|
||||||
## 전문 예시
|
## 전문(메시지) 예시
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -108,5 +111,21 @@ ActivityPub implementation for GNUBOARD 5
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## References
|
||||||
|
* https://www.w3.org/TR/activitypub/
|
||||||
|
* https://www.w3.org/TR/activitystreams-core/
|
||||||
|
* https://www.w3.org/TR/activitystreams-vocabulary/
|
||||||
|
* https://github.com/w3c/activitypub/issues/194
|
||||||
|
* https://docs.joinmastodon.org/spec/webfinger/
|
||||||
|
* https://organicdesign.nz/ActivityPub_Code
|
||||||
|
* https://socialhub.activitypub.rocks/t/posting-to-pleroma-inbox/1184
|
||||||
|
* https://github.com/broidHQ/integrations/tree/master/broid-schemas#readme
|
||||||
|
* https://github.com/autogestion/pubgate-telegram
|
||||||
|
* https://docs.joinmastodon.org/spec/security/
|
||||||
|
* https://chat.openai.com/share/4fda7974-cc0b-439a-b0f2-dc828f8acfef
|
||||||
|
* https://codeberg.org/mro/activitypub/src/commit/4b1319d5363f4a836f23c784ef780b81bc674013/like.sh#L101
|
||||||
|
* https://socialhub.activitypub.rocks/t/problems-posting-to-mastodon-inbox/801/10
|
||||||
|
|
||||||
## 문의
|
## 문의
|
||||||
* gnh1201@gmail.com
|
* abuse@catswords.net
|
||||||
|
* ActivityPub [@gnh1201@catswords.social](https://catswords.social/@gnh1201)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
112
lib/chatgpt.activitypub.lib.php
Normal file
112
lib/chatgpt.activitypub.lib.php
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
<?php
|
||||||
|
// GhatGPT-ActivityPub implementation for GNUBOARD 5
|
||||||
|
// Go Namhyeon <abuse@catswords.net>
|
||||||
|
// MIT License
|
||||||
|
// 2023-02-16
|
||||||
|
|
||||||
|
if (!defined('_GNUBOARD_') || !defined("ACTIVITYPUB_INSTANCE_ID")) exit; // 개별 페이지 접근 불가
|
||||||
|
|
||||||
|
// ChatGPT API 키 발급: https://platform.openai.com/account/api-keys
|
||||||
|
define("CHATGPT_API_KEY", "YOUR_API_KEY"); // API 키 입력
|
||||||
|
define("CHATGPT_API_URL", "https://api.openai.com/v1/completions"); // GhatGPT API 주소 입력
|
||||||
|
define("LINGVA_API_URL", "https://lingva.ml/api/v1"); // Lingva Translate (구글 번역기 프론트엔드) API 주소 입력
|
||||||
|
|
||||||
|
function lingva_translate($content, $source = 'ko', $target = 'en') {
|
||||||
|
$handle = curl_init();
|
||||||
|
curl_setopt($handle, CURLOPT_URL, LINGVA_API_URL . '/' . $source . '/' . $target . '/' . urlencode($content));
|
||||||
|
curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
$response = json_decode(curl_exec($handle), true);
|
||||||
|
curl_close($handle);
|
||||||
|
return $response['translation'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function lingva_ko2en($content) {
|
||||||
|
return lingva_translate($content, 'ko', 'en');
|
||||||
|
}
|
||||||
|
|
||||||
|
function lingva_en2ko($content) {
|
||||||
|
return lingva_translate($content, 'en', 'ko');
|
||||||
|
}
|
||||||
|
|
||||||
|
function chatgpt_request($content, $mb) {
|
||||||
|
// "What is the capital of France?"
|
||||||
|
$prompt = lingva_ko2en(filter_var($content, FILTER_SANITIZE_STRING));
|
||||||
|
$prompt = filter_var($content, FILTER_SANITIZE_STRING);
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
"model" => "text-davinci-003",
|
||||||
|
"prompt" => $prompt,
|
||||||
|
"max_tokens" => 3000,
|
||||||
|
"temperature" => 0.5,
|
||||||
|
);
|
||||||
|
|
||||||
|
$data_string = json_encode($data);
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, CHATGPT_API_URL);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||||
|
"Content-Type: application/json",
|
||||||
|
"Authorization: Bearer " . CHATGPT_API_KEY,
|
||||||
|
"Content-Length: " . strlen($data_string))
|
||||||
|
);
|
||||||
|
|
||||||
|
$output = curl_exec($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
// print_r($output);
|
||||||
|
|
||||||
|
$output_json = json_decode($output, true);
|
||||||
|
$response = $output_json["choices"][0]["text"];
|
||||||
|
// echo $response;
|
||||||
|
|
||||||
|
return lingva_en2ko($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
function chatgpt_send_conversation($content) {
|
||||||
|
global $member;
|
||||||
|
|
||||||
|
// 로그인 상태인 경우 해당 회원, 아닌 경우 ActivityPub 공통 계정
|
||||||
|
$mb = isset($member['mb_id']) ? $member : get_member(ACTIVITYPUB_G5_USERNAME);
|
||||||
|
|
||||||
|
// 수신자 목록
|
||||||
|
$to = array();
|
||||||
|
|
||||||
|
// 참고: 아래에 기술된 역할은 외부 프로그램이 담당하게 할 수도 있음 (service:chatgpt)
|
||||||
|
if (!empty(CHATGPT_API_KEY)) {
|
||||||
|
$response = chatgpt_request($content, $mb);
|
||||||
|
$to[] = "service:chatgpt";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activity 발행 (발신: 그누보드5 -> ChatGPT)
|
||||||
|
activitypub_publish_content(
|
||||||
|
$content,
|
||||||
|
activitypub_get_url("user", array("mb_id" => $mb['mb_id'])),
|
||||||
|
get_member(ACTIVITYPUB_G5_USERNAME),
|
||||||
|
array(),
|
||||||
|
$to
|
||||||
|
);
|
||||||
|
|
||||||
|
// Activity 발행 (수신: ChatGPT -> 그누보드5)
|
||||||
|
activitypub_publish_content(
|
||||||
|
$response,
|
||||||
|
"service:chatgpt",
|
||||||
|
get_member(ACTIVITYPUB_G5_USERNAME),
|
||||||
|
array(),
|
||||||
|
array(
|
||||||
|
activitypub_get_url("user", array("mb_id" => $mb['mb_id']))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _chatgpt_memo_form_update_after($member_list, $str_nick_list, $redirect_url, $me_memo) {
|
||||||
|
// 수신자에 'apstreams' 계정이 있는지 확인
|
||||||
|
if (!in_array(ACTIVITYPUB_G5_USERNAME, $member_list['id'])) return;
|
||||||
|
|
||||||
|
// ChatGPT에게 대화 걸기
|
||||||
|
chatgpt_send_conversation($me_memo);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_event("memo_form_update_after", "_chatgpt_memo_form_update_after", 1, 4);
|
167
lib/webhook.activitypub.lib.php
Normal file
167
lib/webhook.activitypub.lib.php
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
|
||||||
|
|
||||||
|
// ActivityPub/WebHook implementation for GNUBOARD 5
|
||||||
|
// Go Namhyeon <abuse@catswords.net>
|
||||||
|
// MIT License
|
||||||
|
// 2022-07-07
|
||||||
|
|
||||||
|
// [Reference]
|
||||||
|
// * https://github.com/gnh1201/reasonableframework/blob/master/helper/webhooktool.php
|
||||||
|
|
||||||
|
// `NateOn` is trademark of SK Communications Co Ltd., SK Planet Co Ltd.
|
||||||
|
// `Discord' is trademark of Discord Inc. (Former, Hammer And Chisel)
|
||||||
|
// `Slack` is trademark of Slack Technologies Inc.
|
||||||
|
|
||||||
|
// 내려받기: https://sir.kr/g5_plugin/10381
|
||||||
|
if (!defined("ACTIVITYPUB_INSTANCE_ID")) return;
|
||||||
|
|
||||||
|
define("NATEON_WEBHOOK_URL", "");
|
||||||
|
define("DISCORD_WEBHOOK_URL", "");
|
||||||
|
define("SLACK_WEBHOOK_URL", "");
|
||||||
|
define("SLACK_WEBHOOK_CHANNEL", "#webhook");
|
||||||
|
|
||||||
|
function nateon_send_webhook($content, $mb) {
|
||||||
|
$headers = array(
|
||||||
|
"Content-Type" => "application/x-www-form-urlencoded",
|
||||||
|
);
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt_array($ch, array(
|
||||||
|
CURLOPT_URL => NATEON_WEBHOOK_URL,
|
||||||
|
CURLOPT_HTTPHEADER => activitypub_build_http_headers($headers),
|
||||||
|
CURLOPT_SSL_VERIFYPEER => false,
|
||||||
|
CURLOPT_CONNECTTIMEOUT => 10,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_POSTFIELDS => "content=" . urlencode($content),
|
||||||
|
CURLOPT_POST => true
|
||||||
|
));
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
return array("status" => $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
function discord_send_webhook($content, $mb) {
|
||||||
|
$headers = array(
|
||||||
|
"Content-Type" => "application/json",
|
||||||
|
);
|
||||||
|
|
||||||
|
$rawdata = activitypub_json_encode(array(
|
||||||
|
"content" => $content,
|
||||||
|
"username" => $mb['mb_nick']
|
||||||
|
));
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt_array($ch, array(
|
||||||
|
CURLOPT_URL => DISCORD_WEBHOOK_URL,
|
||||||
|
CURLOPT_HTTPHEADER => activitypub_build_http_headers($headers),
|
||||||
|
CURLOPT_SSL_VERIFYPEER => false,
|
||||||
|
CURLOPT_CONNECTTIMEOUT => 10,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_POSTFIELDS => $rawdata,
|
||||||
|
CURLOPT_POST => true
|
||||||
|
));
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
return array("status" => $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
function slack_send_webhook($content, $mb) {
|
||||||
|
$headers = array(
|
||||||
|
"Content-Type" => "application/json",
|
||||||
|
);
|
||||||
|
|
||||||
|
$rawdata = activitypub_json_encode(array(
|
||||||
|
"channel" => SLACK_WEBHOOK_CHANNEL,
|
||||||
|
"username" => $mb['mb_nick'],
|
||||||
|
"text" => $content,
|
||||||
|
"icon_emoji" => ":ghost:"
|
||||||
|
));
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt_array($ch, array(
|
||||||
|
CURLOPT_URL => SLACK_WEBHOOK_URL,
|
||||||
|
CURLOPT_HTTPHEADER => activitypub_build_http_headers($headers),
|
||||||
|
CURLOPT_SSL_VERIFYPEER => false,
|
||||||
|
CURLOPT_CONNECTTIMEOUT => 10,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_POSTFIELDS => $rawdata,
|
||||||
|
CURLOPT_POST => true
|
||||||
|
));
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
return array("status" => $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
function send_webhooks($content) {
|
||||||
|
global $member;
|
||||||
|
|
||||||
|
// 로그인 상태인 경우 해당 회원, 아닌 경우 ActivityPub 공통 계정
|
||||||
|
$mb = isset($member['mb_id']) ? $member : get_member(ACTIVITYPUB_G5_USERNAME);
|
||||||
|
|
||||||
|
// 수신자 목록
|
||||||
|
$to = array();
|
||||||
|
|
||||||
|
// 참고: 아래 함수의 역할은 외부 프로그램이 담당하게 할 수도 있음 (ActivityPub Outbox로부터 데이터 받은 후 처리 가능)
|
||||||
|
// nateon_send_webhook, discord_send_webhook, slack_send_webhook
|
||||||
|
|
||||||
|
if (!empty(NATEON_WEBHOOK_URL))
|
||||||
|
nateon_send_webhook($content, $mb);
|
||||||
|
$to[] = "webhook:nateon";
|
||||||
|
|
||||||
|
if (!empty(DISCORD_WEBHOOK_URL))
|
||||||
|
discord_send_webhook($content, $mb);
|
||||||
|
$to[] = "webhook:discord";
|
||||||
|
|
||||||
|
if (!empty(SLACK_WEBHOOK_URL))
|
||||||
|
slack_send_webhook($content, $mb);
|
||||||
|
$to[] = "webhook:slack";
|
||||||
|
|
||||||
|
// Activity로 발행
|
||||||
|
activitypub_publish_content(
|
||||||
|
$content,
|
||||||
|
activitypub_get_url("user", array("mb_id" => $mb['mb_id'])),
|
||||||
|
get_member(ACTIVITYPUB_G5_USERNAME),
|
||||||
|
array(),
|
||||||
|
$to
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _webhook_memo_form_update_after($member_list, $str_nick_list, $redirect_url, $me_memo) {
|
||||||
|
send_webhooks($me_memo); // 웹훅 보내기
|
||||||
|
}
|
||||||
|
|
||||||
|
function _webhook_write_update_after($board, $wr_id, $w, $qstr, $redirect_url) {
|
||||||
|
global $g5;
|
||||||
|
|
||||||
|
// 본문 가져오기
|
||||||
|
$sql = "select wr_id, wr_content from {$g5['write_prefix']}{$board['bo_table']} where wr_id = '{$wr_id}'";
|
||||||
|
$row = sql_fetch($sql);
|
||||||
|
if (empty($row['wr_id'])) return;
|
||||||
|
|
||||||
|
// 웹훅 보내기
|
||||||
|
send_webhooks($row['wr_content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _webhook_comment_update_after($board, $wr_id, $w, $qstr, $redirect_url, $comment_id, $reply_array) {
|
||||||
|
global $g5;
|
||||||
|
|
||||||
|
// 본문(댓글) 가져오기
|
||||||
|
$sql = "select wr_id, wr_parent, wr_content from {$g5['write_prefix']}{$board['bo_table']} where wr_id = '{$comment_id}'";
|
||||||
|
$row = sql_fetch($sql);
|
||||||
|
if (empty($row['wr_id'])) return;
|
||||||
|
|
||||||
|
// 웹훅 보내기
|
||||||
|
send_webhooks($row['wr_content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_event("write_update_after", "_webhook_write_update_after", 1, 5);
|
||||||
|
add_event("comment_update_after", "_webhook_comment_update_after", 1, 7);
|
||||||
|
add_event("memo_form_update_after", "_webhook_memo_form_update_after", 1, 4);
|
Loading…
Reference in New Issue
Block a user