PawchiveAPI

The `PawchiveAPI` class provides a simple and efficient way to interact with the Pawchive API.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         PawchiveAPI
// @namespace    Beginner.2023.PawchiveAPI
// @version      1.0.4a
// @description  The `PawchiveAPI` class provides a simple and efficient way to interact with the Pawchive API.
// @author       Beginner(2023)
// @match        *://pawchive.pw/*
// @match        *://*/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=pawchive.pw
// @grant        GM_xmlhttpRequest
// @connect      pawchive.pw
// @license      MIT
// ==/UserScript==

(function () {


    class PawchiveAPI {
        constructor(baseUrl = 'https://pawchive.pw/api/v1/', headers = {}, debug = true) {
            this.baseUrl = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
            this.defaultHeaders = {
                'Accept': 'application/json',
                ...headers
            };
            this.debug = debug;
        }

        #request(endpoint, options = {}) {
            return new Promise((resolve, reject) => {
                const cleanEndpoint = endpoint.startsWith('/') ? endpoint.slice(1) : endpoint;
                const url = `${this.baseUrl}${cleanEndpoint}`;
                const method = options.method || 'GET';

                const headers = {
                    ...this.defaultHeaders,
                    ...options.headers
                };

                if (this.debug) {
                    console.log(`[PawchiveAPI] 🚀 Requesting: [${method}] ${url}`);
                }

                const startTime = performance.now();

                GM_xmlhttpRequest({
                    method: method,
                    url: url,
                    headers: headers,
                    data: options.body ? JSON.stringify(options.body) : undefined,
                    onload: (response) => {
                        const endTime = performance.now();
                        const duration = (endTime - startTime).toFixed(2);

                        if (this.debug) {
                            console.log(`[PawchiveAPI] 📡 Status: ${response.status} ${response.statusText} (${duration}ms)`);
                        }

                        if (response.status >= 200 && response.status < 300) {
                            let data;
                            try {
                                data = JSON.parse(response.responseText);
                            } catch (e) {
                                data = response.responseText;
                            }

                            if (this.debug) {
                                console.log(`[PawchiveAPI] 📦 Data Received:`, data);
                            }
                            resolve(data);
                        } else {
                            if (this.debug) {
                                console.error(`[PawchiveAPI Error] ❌ Request failed for ${url}: Status ${response.status}`);
                            }
                            reject(new Error(`HTTP Error! Status: ${response.status} - ${response.statusText}`));
                        }
                    },
                    onerror: (error) => {
                        if (this.debug) {
                            console.error(`[PawchiveAPI Error] ❌ Request failed for ${url}:`, error);
                        }
                        reject(new Error(`Network request failed for ${url}`));
                    },
                    ontimeout: () => {
                        reject(new Error(`Request timed out for ${url}`));
                    }
                });
            });
        }

        #buildEndpoint(path, params = {}) {
            const query = new URLSearchParams(params).toString();
            return query ? `${path}?${query}` : path;
        }

        async getCreators() {
            return this.#request('creators');
        }

        async getRecentPosts(params = {}) {
            return this.#request(this.#buildEndpoint('posts', params));
        }

        async getCreatorPosts(service, creatorId, params = {}) {
            return this.#request(this.#buildEndpoint(`${service}/user/${creatorId}`, params));
        }

        async getCreatorAnnouncements(service, creatorId) {
            return this.#request(`${service}/user/${creatorId}/announcements`);
        }

        async getCreatorFancards(service, creatorId) {
            return this.#request(`${service}/user/${creatorId}/fancards`);
        }

        async getPost(service, creatorId, postId) {
            return this.#request(`${service}/user/${creatorId}/post/${postId}`);
        }

        async getPostRevisions(service, creatorId, postId) {
            return this.#request(`${service}/user/${creatorId}/post/${postId}/revisions`);
        }

        async getCreatorProfile(service, creatorId) {
            return this.#request(`${service}/user/${creatorId}/profile`);
        }

        async getCreatorLinks(service, creatorId) {
            return this.#request(`${service}/user/${creatorId}/links`);
        }

        async getPostComments(service, creatorId, postId) {
            return this.#request(`${service}/user/${creatorId}/post/${postId}/comments`);
        }

        async flagPost(service, creatorId, postId) {
            return this.#request(`${service}/user/${creatorId}/post/${postId}/flag`, {
                method: 'POST'
            });
        }

        async checkPostFlagged(service, creatorId, postId) {
            return this.#request(`${service}/user/${creatorId}/post/${postId}/flag`);
        }

        async getAccountFavorites() {
            return this.#request('account/favorites');
        }

        async addFavoritePost(service, creatorId, postId) {
            return this.#request(`favorites/post/${service}/${creatorId}/${postId}`, {
                method: 'POST'
            });
        }

        async removeFavoritePost(service, creatorId, postId) {
            return this.#request(`favorites/post/${service}/${creatorId}/${postId}`, {
                method: 'DELETE'
            });
        }

        async addFavoriteCreator(service, creatorId) {
            return this.#request(`favorites/creator/${service}/${creatorId}`, {
                method: 'POST'
            });
        }

        async removeFavoriteCreator(service, creatorId) {
            return this.#request(`favorites/creator/${service}/${creatorId}`, {
                method: 'DELETE'
            });
        }

        async getAppVersion() {
            return this.#request('app_version');
        }
    }

    if (typeof unsafeWindow !== 'undefined') {
        unsafeWindow.PawchiveAPI = PawchiveAPI;
        unsafeWindow.pawchiveAPI = PawchiveAPI;
    }

    window.PawchiveAPI = PawchiveAPI;
    window.pawchiveAPI = PawchiveAPI;

})();