import hmac
import json
from hashlib import sha1

import requests
from time import timezone
from time import time as timestamp
from json_minify import json_minify

import os
import base64
from uuid import UUID
from typing import BinaryIO
from binascii import hexlify

from .lib import *
from .lib import CheckExceptions

# Created By Bovonos -_-
# Solved-Rewrited-Edited By SirLez too Many Times -_-


def ndc(data):
    mac = hmac.new(bytes.fromhex("307c3c8cd389e69dc298d951341f88419a8377f4"), data.encode("utf-8"), sha1)
    return base64.b64encode(bytes.fromhex("22") + mac.digest()).decode("utf-8")


class Local:
    def __init__(self, comId: str):
        self._api = 'https://service.narvii.com/api/v1'
        self.api = 'https://aminoapps.com/api-p'
        if comId is None: self.comId = None
        elif comId is not None: self.comId = comId
        self.uid = headers.Headers().uid
        headers.comId = self.comId
        self.headers = headers.Headers().headers
        self.web_headers = headers.Headers().web_headers
        self._headers = headers.Headers()._headers

    # By SirLez & Bovonos
    def get_video_rep_info(self, chatId: str):
        req = requests.get(f"https://aminoapps.com/api-p/x{self.comId}/s/chat/thread/{chatId}/avchat-reputation", headers=self._headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return RepInfo(req.json())

    # By SirLez & Bovonos
    def claim_video_rep(self, chatId: str):
        info = self.get_video_rep_info(chatId)
        reputation = info.json["reputation"]
        if int(reputation) < 1: return CheckExceptions(f"reputation should be more than 1 (Your rep Now {int(reputation)}")
        req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/avchat-reputation", headers=self._headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return Rep(req.json())

    def join_chat(self, chatId: str = None, asWeb: bool = False):
        if asWeb:
            data = {"ndcId": "x"+self.comId, "threadId": chatId}
            req = requests.post("https://aminoapps.com/api/join-thread", json=data, headers=self.web_headers)
            try:
                if "OK" not in req.json()["result"]["api:message"]: return CheckExceptions(req.json())
                else: Json(req.json())
            except: return CheckExceptions(req.json())
            return Json(req.json())

        req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/member/{self.uid}", headers=self._headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return Json(req.json())

    def upload_media(self, file: BinaryIO, fileType: str):
        if fileType == "audio": type = "audio/aac"
        elif fileType == "image": type = "image/jpg"
        else: raise TypeError("")

        data = file.read()
        header = self.headers
        header["content-type"] = type
        header["content-length"] = str(len(data))

        req = requests.post(f"{self._api}/g/s/media/upload", data=data, headers=header)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return req.json()["mediaValue"]

    def leave_chat(self, chatId: str = None):
        req = requests.delete(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/member/{self.uid}", headers=self._headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return Json(req.json())

    def get_member_following(self, userId: str = None, start: int = 0, size: int = 25):
        req = requests.get(f"{self._api}/x{self.comId}/s/user-profile/{userId}/joined?start={start}&size={size}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return UserList(req.json()['userProfileList'])

    def get_member_followers(self, userId: str = None, start: int = 0, size: int = 25):
        req = requests.get(f"{self._api}/x{self.comId}/s/user-profile/{userId}/joined?start={start}&size={size}")
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return UserList(req.json()['userProfileList'])

    def get_chat_threads(self, start: int = 0, size: int = 25):
        req = requests.get(f'{self._api}/x{self.comId}/s/chat/thread?type=joined-me&start={start}&size={size}', headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return MyChats(req.json()['threadList'])

    def get_member_visitors(self, userId: str, start: int = 0, size: int = 25):
        req = requests.get(f'{self._api}/x{self.comId}/s/user-profile/{userId}/visitors?start={start}&size={size}', headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Visitors(req.json()['visitors'])

    def get_chat_messages(self, chatId: str, size: int = 25):
        req = requests.get(f'{self._api}/x{self.comId}/s/chat/thread/{chatId}/message?v=2&pagingType=t&size={size}', headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return ChatMessages(req.json()['messageList'])

    def get_user_info(self, userId: str):
        req = requests.get(f"{self._api}/x{self.comId}/s/user-profile/{userId}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return UserInfo(req.json()['userProfile'])

    def get_wall_comments(self, userId: str, sorting: str, start: int = 0, size: int = 25):
        sorting = sorting.lower()

        if sorting == "newest": sorting = "newest"
        elif sorting == "oldest": sorting = "oldest"
        elif sorting == "top": sorting = "vote"
        else: raise TypeError("حط تايب يا حمار")  # Not me typed this its (a7rf)

        req = requests.get(f"{self._api}/x{self.comId}/s/user-profile/{userId}/comment?sort={sorting}&start={start}&size={size}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Comment(req.json()['commentList'])

    def get_all_users(self, type: str = "recent", start: int = 0, size: int = 25):
        if type == "recent": type = "recent"
        elif type == "banned": type = "banned"
        elif type == "featured": type = "featured"
        elif type == "leaders": type = "leaders"
        elif type == "curators": type = "curators"

        req = requests.get(f'{self._api}/x{self.comId}/s/user-profile?type={type}&start={start}&size={size}', headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return UserList(req.json()['userProfileList'])

    def get_chat_members(self, start: int = 0, size: int = 25, chatId: str = None):
        req = requests.get(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/member?start={start}&size={size}&type=default&cv=1.2", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return UserList(req.json()['memberList'])

    def get_chat_info(self, chatId: str):
        req = requests.get(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return ChatInfo(req.json()['thread'])

    def get_online_users(self, start: int = 0, size: int = 25):
        req = requests.get(f"{self._api}/x{self.comId}/s/live-layer?topic=ndtopic:x{self.comId}:online-members&start={start}&size={size}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return UserList(req.json()['userProfileList'])

    def get_public_chats(self, type: str = "recommended", start: int = 0, size: int = 50):
        req = requests.get(f'{self._api}/x{self.comId}/s/chat/thread?type=public-all&filterType={type}&start={start}&size={size}', headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return MyChats(req.json()['threadList'])

    def send_message(self, chatId: str, message: str = None, messageType: int = 0, file: BinaryIO = None, fileType: str = None, replyTo: str = None, mentionUserIds: list = None, stickerId: str = None, embedId: str = None, embedType: int = None, embedLink: str = None, embedTitle: str = None, embedContent: str = None, embedImage: BinaryIO = None):
        if message is not None and file is None: message = message.replace("<$", "‎‏").replace("$>", "‬‭")

        mentions = []
        if mentionUserIds:
            for mention_uid in mentionUserIds: mentions.append({"uid": mention_uid})

        if embedImage: embedImage = [[100, self.upload_media(embedImage, "image"), None]]

        data = {
            "type": messageType,
            "content": message,
            "attachedObject": {
                "objectId": embedId,
                "objectType": embedType,
                "link": embedLink,
                "title": embedTitle,
                "content": embedContent,
                "mediaList": embedImage
            },
            "extensions": {"mentionedArray": mentions},
            "timestamp": int(timestamp() * 1000)
        }

        if replyTo: data["replyMessageId"] = replyTo

        if stickerId:
            data["content"] = None
            data["stickerId"] = stickerId
            data["type"] = 3

        if file:
            data["content"] = None
            if fileType == "audio":
                data["type"] = 2
                data["mediaType"] = 110

            elif fileType == "image":
                data["mediaType"] = 100
                data["mediaUploadValueContentType"] = "image/jpg"
                data["mediaUhqEnabled"] = True

            elif fileType == "gif":
                data["mediaType"] = 100
                data["mediaUploadValueContentType"] = "image/gif"
                data["mediaUhqEnabled"] = True

            else:
                raise TypeError("")

            data["mediaUploadValue"] = base64.b64encode(file.read()).decode()

        data = json.dumps(data)
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/message", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def send_web_message(self, chatId: str, message: str = None, messageType: int = 0, icon: str = None, comId: str = None):
        if comId: self.comId = comId

        data = {
            "ndcId": f"x{self.comId}",
            "threadId": chatId,
            "message": {"content": message, "mediaType": 0, "type": messageType, "sendFailed": False, "clientRefId": 0}
        }

        if icon:
            data["message"]["content"] = None
            data["message"]["uploadId"] = 0
            data["message"]["mediaType"] = 100
            data["message"]["mediaValue"] = icon

        req = requests.post("https://aminoapps.com/api/add-chat-message", json=data, headers=self.web_headers)
        try:
            if req.json()["code"] != 200: return CheckExceptions(req.json())
            else: Json(req.json())
        except: return CheckExceptions(req.json())
        return Json(req.json())

    def unfollow(self, userId: str, asWeb: bool = False):
        if asWeb:
            data = {
                "ndcId": f"x{self.comId}",
                "followee_id": userId,
                "follower_id": self.uid
            }
            req = requests.post("https://aminoapps.com/api/unfollow-user", json=data, headers=self.web_headers)
            try:
                if req.json()["code"] != 200: return CheckExceptions(req.json())
                else: Json(req.json())
            except: return CheckExceptions(req.json())
            return Json(req.json())

        req = requests.post(f"{self._api}/g/s/user-profile/{userId}/member/{self.uid}", headers=self._headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def follow(self, userId: [str, list], asWeb: bool = False):
        if asWeb:
            data = {
                "ndcId": f"x{self.comId}",
                "followee_id": userId
            }
            req = requests.post("https://aminoapps.com/api/follow-user", json=data, headers=self.web_headers)
            try:
                if req.json()["code"] != 200: return CheckExceptions(req.json())
                else: Json(req.json())
            except: return CheckExceptions(req.json())
            return Json(req.json())

        data = {"timestamp": int(timestamp() * 1000)}

        if isinstance(userId, str): api = f"{self._api}/x{self.comId}/s/user-profile/{userId}/member"
        if isinstance(userId, list): api = f'{self._api}/x{self.comId}/s/user-profile/{self.uid}/joined'; data = json.dumps({"targetUidList": userId, "timestamp": int(timestamp() * 1000)})
        sig = ndc(data)
        req = requests.post(api, headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def start_chat(self, userId: [str, list], title: str = None, message: str = None, content: str = None, type: int = 0, asWeb: bool = False):
        if isinstance(userId, str): userIds = [userId]
        elif isinstance(userId, list): userIds = userId
        else: raise TypeError("")
        if asWeb:
            data = {
                "ndcId": self.comId,
                "inviteeUids": userIds,
                "initialMessageContent": message,
                "type": type
            }
            req = requests.post("https://aminoapps.com/api/create-chat-thread", json=data, headers=self.web_headers)
            try:
                if "OK" not in req.json()["result"]["api:message"]: return CheckExceptions(req.json())
                else: Json(req.json())
            except: return CheckExceptions(req.json())
            return Json(req.json())

        data = json.dumps({
            "title": title,
            "inviteeUids": userIds,
            "initialMessageContent": message,
            "content": content,
            "type": type,
            "timestamp": int(timestamp() * 1000)
        })
        sig = ndc(data)
        req = requests.post(f'{self._api}/x{self.comId}/s/chat/thread', headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def invite_to_chat(self, userId: [str, list], chatId: str = None):
        if isinstance(userId, str): userIds = [userId]
        elif isinstance(userId, list): userIds = userId
        else: raise TypeError("")

        data = json.dumps({"uids": userIds, "timestamp": int(timestamp() * 1000)})
        sig = ndc(data)
        req = requests.post(f'{self._api}/x{self.comId}/s/chat/thread/{chatId}/member/invite', data=data, headers=headers.Headers(data=data, sig=sig).headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def edit_profile(self, nickname: str = None, content: str = None, icon: str = None):
        data = {
            "latitude": 0,
            "longitude": 0,
            "eventSource": "UserProfileView",
            "timestamp": int(timestamp() * 1000)
        }

        if nickname: data["nickname"] = nickname
        if content: data["content"] = content
        if icon: data["icon"] = icon

        data = json.dumps(data)
        sig = ndc(data)
        req = requests.post(f'{self._api}/x{self.comId}/s/user-profile/{self.uid}', headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def edit_chat(self, chatId: str, title: str = None, content: str = None, icon: str = None, background: str = None, keywords: list = None, announcement: str = None, pinAnnouncement: bool = None):
        res, data = [], {"timestamp": int(timestamp() * 1000)}

        if title: data["title"] = title
        if content: data["content"] = content
        if icon: data["icon"] = icon
        if keywords: data["keywords"] = keywords
        if announcement: data["extensions"] = {"announcement": announcement}
        if pinAnnouncement: data["extensions"] = {"pinAnnouncement": pinAnnouncement}
        if background:
            data = json.dumps({"media": [100, background, None], "timestamp": int(timestamp() * 1000)})
            sig = ndc(data)
            req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/member/{self.uid}/background", data=data, headers=headers.Headers(data=data, sig=sig).headers)
            if req.status_code != 200: return CheckExceptions(req.json())
            res.append(Json(req.json()))

        data = json.dumps(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}", data=data, headers=headers.Headers(data=data, sig=sig).headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        res.append(Json(req.json()))
        return res

    def chat_settings(self, chatId: str, viewOnly: bool = None, doNotDisturb: bool = None, canInvite: bool = False, canTip: bool = None, pin: bool = None):
        res = []

        if doNotDisturb is not None:
            if doNotDisturb: opt = 2
            if not doNotDisturb: opt = 1
            data = json.dumps({"alertOption": opt})
            req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/member/{self.uid}/alert", data=data, headers=self._headers)
            if req.status_code != 200: return CheckExceptions(req.json())
            res.append(Json(req.json()))

        if viewOnly is not None:
            if viewOnly: viewOnly = "enable"
            if not viewOnly: viewOnly = "disable"
            req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/view-only/{viewOnly}", headers=self.headers)
            if req.status_code != 200: return CheckExceptions(req.json())
            res.append(Json(req.json()))

        if canInvite is not None:
            if canInvite: canInvite = "enable"
            if not canInvite: canInvite = "disable"
            req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/members-can-invite/{canInvite}", headers=self._headers)
            if req.status_code != 200: return CheckExceptions(req.json())
            res.append(Json(req.json()))

        if canTip is not None:
            if canTip: canTip = "enable"
            if not canTip: canTip = "disable"
            req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/tipping-perm-status/{canTip}", headers=self._headers)
            if req.status_code != 200: return CheckExceptions(req.json())
            res.append(Json(req.json()))

        if pin is not None:
            if pin: pin = "pin"
            if not pin: pin = "unpin"
            req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/{pin}", headers=self._headers)
            if req.status_code != 200: return CheckExceptions(req.json())
            res.append(Json(req.json()))

        return res

    def vote_comment(self, blogId: str, commentId: str, value: int = 1):
        """
        vote & unvote comment.

          **Parameters**
              - **blogId** : Id of the blog.
              - **commentId** : Comment id.
              - **value** : 1 for upvote -1 for downvote.

          **Returns**
              - **Success** : :meth:`Json Object <samino.lib.objects.Json>`

              - **Fail** : :meth:`Exceptions <samino.lib.exceptions>`
        """
        if value != 1: pass
        elif value != -1: raise TypeError(f"Please fill the value by (-1 or 1) not ({value})")
        data = json.dumps({"value": value, "timestamp": int(timestamp() * 1000)})
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/blog/{blogId}/comment/{commentId}/vote?cv=1.2&value=1", data=data, headers=headers.Headers(data=data, sig=sig).headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def like_blog(self, blogId: str = None, wikiId: str = None, asWeb: bool = False):
        if asWeb:
            data = {"ndcId": self.comId}
            if blogId: data["logType"] = "blog"; data["postType"] = "blog"; postId = blogId
            if wikiId: data["logType"] = "wiki"; data["postType"] = "wiki"; postId = wikiId
            data["postId"] = postId
            req = requests.post("https://aminoapps.com/api/vote", json=data, headers=self.web_headers)
            try:
                if "OK" not in req.json()["result"]["api:message"]: return CheckExceptions(req.json())
                else: Json(req.json())
            except: return CheckExceptions(req.json())
            return Json(req.json())

        data = json.dumps({"value": 4, "timestamp": int(timestamp() * 1000)})
        if blogId: api = f"{self._api}/x{self.comId}/s/blog/{blogId}/vote?cv=1.2&value=4"
        if wikiId: api = f"{self._api}/x{self.comId}/s/item/{wikiId}/vote?cv=1.2&value=4"
        sig = ndc(data)
        req = requests.post(api, headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def unlike_blog(self, blogId: str = None, wikiId: str = None, asWeb: bool = False):
        if asWeb:
            data = {"ndcId": self.comId, }
            if blogId: data["logType"] = "blog"; data["postType"] = "blog"; postId = blogId
            if wikiId: data["logType"] = "wiki"; data["postType"] = "wiki"; postId = wikiId
            data["postId"] = postId
            req = requests.post("https://aminoapps.com/api/unvote", json=data, headers=self.web_headers)
            try:
                if "OK" not in req.json()["result"]["api:message"]: return CheckExceptions(req.json())
                else: Json(req.json())
            except: return CheckExceptions(req.json())
            return Json(req.json())

        if blogId: api = f"{self._api}/x{self.comId}/s/blog/{blogId}/vote?eventSource=FeedList"
        if wikiId: api = f"{self._api}/x{self.comId}/s/item/{wikiId}/vote?eventSource=FeedList"
        req = requests.delete(api, headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def change_titles(self, userId: str, titles: list, colors: list):
        t = []
        for title, color in zip(titles, colors): t.append({"title": title, "color": color})
        data = json.dumps({
            "adminOpName": 207,
            "adminOpValue": {"titles": t},
            "timestamp": int(timestamp() * 1000)
        })
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/user-profile/{userId}/admin", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def like_comment(self, commentId: str, blogId: str = None, wikiId: str = None, userId: str = None):
        data = json.dumps({"value": 1})
        if blogId: api = f"{self._api}/x{self.comId}/s/blog/{blogId}/comment/{commentId}/vote?cv=1.2&value=1"
        if wikiId: api = f"{self._api}/x{self.comId}/s/item/{wikiId}/comment/{commentId}/vote?cv=1.2&value=1"
        if userId: api = f"{self._api}/x{self.comId}/s/user-profile/{userId}/comment/{commentId}/vote?cv=1.2&value=1"
        req = requests.post(api, data=data, headers=headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def unlike_comment(self, commentId: str, blogId: str = None, wikiId: str = None, userId: str = None):
        if blogId: api = f"{self._api}/x{self.comId}/s/blog/{blogId}/comment/{commentId}/g-vote?eventSource=PostDetailView"
        if wikiId: api = f"{self._api}/x{self.comId}/s/item/{wikiId}/comment/{commentId}/g-vote?eventSource=PostDetailView"
        if userId: api = f"{self._api}/x{self.comId}/s/user-profile/{userId}/comment/{commentId}/g-vote?eventSource=UserProfileView"
        req = requests.delete(api, headers=headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def comment(self, comment: str, userId: str = None, blogId: str = None, wikiId: str = None, replyTo: str = None, isGuest: bool = False, asWeb: bool = False):
        if asWeb:
            data = {"ndcId": self.comId, "content": comment}
            if blogId: data["postType"] = "blog"; postId = blogId
            if wikiId: data["postType"] = "wiki"; postId = wikiId
            if userId: data["postType"] = "user"; postId = userId
            data["postId"] = postId

            req = requests.post("https://aminoapps.com/api/submit_comment", json=data, headers=self.web_headers)
            try:
                if "data-comment-id" not in req.json()["result"]["html"]: return CheckExceptions(req.json())
                else: Json(req.json())
            except: return CheckExceptions(req.json())
            return Json(req.json())

        data = {"content": comment, "timestamp": int(timestamp() * 1000)}
        if replyTo: data["respondTo"] = replyTo
        if isGuest: comType = "g-comment"
        else: comType = "comment"
        if userId: api = f"{self._api}/x{self.comId}/s/user-profile/{userId}/{comType}"
        if blogId: api = f"{self._api}/x{self.comId}/s/blog/{blogId}/{comType}"
        if wikiId: api = f"{self._api}/x{self.comId}/s/item/{wikiId}/{comType}"
        data = json.dumps(data)
        sig = ndc(data)
        req = requests.post(api, data=data, headers=headers.Headers(data=data, sig=sig).headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def delete_comment(self, commentId: str, userId: str = None, blogId: str = None, wikiId: str = None):
        if userId: api = f"{self._api}/x{self.comId}/s/user-profile/{userId}/comment/{commentId}"
        if blogId: api = f"{self._api}/x{self.comId}/s/blog/{blogId}/comment/{commentId}"
        if wikiId: api = f"{self._api}/x{self.comId}/s/item/{wikiId}/comment/{commentId}"
        else: raise TypeError(" ")
        req = requests.delete(api, headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def vote_poll(self, blogId: str, optionId: str):
        data = json.dumps({"value": 1, "timestamp": int(timestamp() * 1000)})
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/blog/{blogId}/poll/option/{optionId}/vote", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def get_blog_info(self, blogId: str = None, wikiId: str = None):
        if blogId: api = f"{self._api}/x{self.comId}/s/blog/{blogId}"
        if wikiId: api = f"{self._api}/x{self.comId}/s/item/{wikiId}"
        req = requests.get(api, headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return BlogInfo(req.json()["blog"])

    def get_blogs(self, start: int = 0, size: int = 25):
        req = requests.get(f"{self._api}/x{self.comId}/s/feed/featured?start={start}&size={size}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return BlogList(req.json()["featuredList"])

    def get_blogs_more(self, start: int = 0, size: int = 25):
        req = requests.get(f"{self._api}/x{self.comId}/s/feed/featured-more?start={start}&size={size}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return BlogList(req.json()["blogList"])

    def get_blogs_all(self, start: int = 0, size: int = 25, pagingType: str = "t"):
        req = requests.get(f"{self._api}/x{self.comId}/s/feed/blog-all?pagingType={pagingType}&start={start}&size={size}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return BlogListAll(req.json()["blogList"])

    def tip_coins(self, coins: int, chatId: str = None, blogId: str = None, wikiId: str = None, transactionId: str = str(UUID(hexlify(os.urandom(16)).decode('ascii')))):
        data = {
            "coins": int(coins),
            "tippingContext": {
                "transactionId": transactionId
            },
            "timestamp": int(timestamp() * 1000)
        }
        if chatId: api = f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/tipping"
        if blogId: api = f"{self._api}/x{self.comId}/s/blog/{blogId}/tipping"
        if wikiId: api = f"{self._api}/x{self.comId}/s/tipping"; data["objectType"] = 2; data["objectId"] = wikiId
        data = json.dumps(data)
        sig = ndc(data)
        req = requests.post(api, headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return Json(req.json())

    def check_in(self, timezone: int = 180):
        data = json.dumps({"timezone": timezone, "timestamp": int(timestamp() * 1000)})
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/check-in", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return Json(req.json())

    def check_in_lottery(self, timezone: int = 180):
        data = json.dumps({"timezone": timezone, "timestamp": int(timestamp() * 1000)})
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/check-in/lottery", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return Json(req.json())

    def delete_message(self, chatId: str, messageId: str, asStaff: bool = False, reason: str = None):
        data = {"adminOpName": 102, "timestamp": int(timestamp() * 1000)}
        if asStaff and reason: data["adminOpNote"] = {"content": reason}
        data = json.dumps(data)
        sig = ndc(data)

        if asStaff: req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/message/{messageId}/admin", data=data, headers=headers.Headers(data=data, sig=sig).headers)
        else: req = requests.delete(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/message/{messageId}", headers=self._headers)

        if req.status_code != 200: return CheckExceptions(req.json())
        else: return Json(req.json())

    def invite_by_host(self, chatId: str, userId: [str, list]):
        data = json.dumps({"uidList": userId, "timestamp": int(timestamp() * 1000)})
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/avchat-members", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return Json(req.json())

    def strike(self, userId: str, time: int, title: str = None, reason: str = None):
        data = json.dumps({
            "uid": userId,
            "title": title,
            "content": reason,
            "attachedObject": {
                "objectId": userId,
                "objectType": 0
            },
            "penaltyType": 1,
            "penaltyValue": time,
            "adminOpNote": {},
            "noticeType": 4,
            "timestamp": int(timestamp() * 1000)
        })
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/notice", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def ban(self, userId: str, reason: str, banType: int = None):
        data = json.dumps({
            "reasonType": banType,
            "note": {
                "content": reason
            },
            "timestamp": int(timestamp() * 1000)
        })
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/user-profile/{userId}/ban", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def unban(self, userId: str, reason: str = 'هذا العضو كان شاطر اخر كم يوم'):
        data = json.dumps({
            "note": {"content": reason},
            "timestamp": int(timestamp() * 1000)
        })
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/user-profile/{userId}/unban", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def hide(self, note: str = None, blogId: str = None, userId: str = None, wikiId: str = None, chatId: str = None):
        opN = 110
        opV = 9

        if userId: opN = 18; opV = None; api = f"{self._api}/x{self.comId}/s/user-profile/{userId}/admin"
        if blogId: api = f"{self._api}/x{self.comId}/s/blog/{blogId}/admin"
        if wikiId: api = f"{self._api}/x{self.comId}/s/item/{wikiId}/admin"
        if chatId: api = f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/admin"

        data = {
            "adminOpNote": {"content": note},
            "adminOpName": opN,
            "adminOpValue": opV,
            "timestamp": int(timestamp() * 1000)
        }
        sig = ndc(data)
        req = requests.post(api, headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def unhide(self, note: str = None, blogId: str = None, userId: str = None, wikiId: str = None, chatId: str = None):
        opN = 110
        opV = 0

        if userId: opN = 19; api = f"{self._api}/x{self.comId}/s/user-profile/{userId}/admin"
        if blogId: api = f"{self._api}/x{self.comId}/s/blog/{blogId}/admin"
        if wikiId: api = f"{self._api}/x{self.comId}/s/item/{wikiId}/admin"
        if chatId: api = f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/admin"

        data = {
            "adminOpNote": {"content": note},
            "adminOpName": opN,
            "adminOpValue": opV,
            "timestamp": int(timestamp() * 1000)
        }
        sig = ndc(data)
        req = requests.post(api, headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def send_warning(self, userId: str, reason: str = None):
        data = json.dumps({
            "uid": userId,
            "title": "Custom",
            "content": reason,
            "attachedObject": {
                "objectId": userId,
                "objectType": 0
            },
            "penaltyType": 0,
            "adminOpNote": {},
            "noticeType": 7,
            "timestamp": int(timestamp() * 1000)
        })
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/notice", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def invite_to_voice_chat(self, userId: str = None, chatId: str = None):
        data = json.dumps({"uid": userId, "timestamp": int(timestamp() * 1000)})
        sig = ndc(data)
        req = requests.post(f'{self._api}/g/x{self.comId}/chat/thread/{chatId}/vvchat-presenter/invite', headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def post_blog(self, title: str, content: str, fansOnly: bool = False):
        data = {
            "extensions": {
                "fansOnly": fansOnly
            },
            "content": content,
            "latitude": 0,
            "longitude": 0,
            "title": title,
            "type": 0,
            "contentLanguage": "ar",
            "eventSource": "GlobalComposeMenu",
            "timestamp": int(timestamp() * 1000)
        }
        data = json.dumps(data)
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/blog", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def post_wiki(self, title: str, content: str, fansOnly: bool = False, icon: str = None,
                  backgroundColor: str = None):
        data = {
            "extensions": {
                "fansOnly": fansOnly,
                "props": [],
                "style": {
                    "backgroundColor": backgroundColor
                }
            },
            "content": "",
            "keywords": content,
            "label": title,
            "latitude": 0,
            "longitude": 0,
            "eventSource": "UserProfileView",
            "timestamp": int(timestamp() * 1000)
        }
        if icon: data["icon"] = icon
        data = json.dumps(data)
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/item", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def delete_blog(self, blogId: str):
        req = requests.delete(f'{self._api}/x{self.comId}/s/blog/{blogId}', headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def delete_wiki(self, wikiId: str):
        req = requests.delete(f"{self._api}/x{self.comId}/s/item/{wikiId}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def activate_status(self, status: int = 1):
        data = json.dumps({
            "onlineStatus": status,
            "duration": 86400,
            "timestamp": int(timestamp() * 1000)
        })
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/user-profile/{self.uid}/online-status", data=data, headers=headers.Headers(data=data, sig=sig).headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def subscribe(self, userId: str, autoRenew: str = False, transactionId: str = str(UUID(hexlify(os.urandom(16)).decode('ascii')))):
        data = json.dumps({
            "paymentContext": {
                "transactionId": transactionId,
                "isAutoRenew": autoRenew
            },
            "timestamp": int(timestamp() * 1000)
        })
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/influencer/{userId}/subscribe", data=data, headers=headers.Headers(data=data, sig=sig).headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def submit_wiki(self, wikiId: str, message: str = None):
        data = {
            "message": message,
            "itemId": wikiId,
            "timestamp": int(timestamp() * 1000)
        }
        data = json.dumps(data)
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/knowledge-base-request", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return Json(req.json())

    def edit_blog(self, title: str, content: str, blogId: str = None, wikiId: str = None, fansOnly: bool = False, backgroundColor: str = None):
        data = {
            "title": title,
            "content": content,
            "timestamp": int(timestamp() * 1000)
        }
        if fansOnly: data['extensions'] = {'fansOnly': True}
        if backgroundColor: data['extensions'] = {'backgroundColor': backgroundColor}
        if blogId: api = f"{self._api}/x{self.comId}/s/blog/{blogId}"
        if wikiId: api = f"{self._api}/x{self.comId}/s/item/{wikiId}"
        data = json.dumps(data)
        sig = ndc(data)
        req = requests.post(api, headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        return Json(req.json())

    def get_chat_bubbles(self, start: int = 0, size: int = 20):
        req = requests.get(f"{self._api}/x{self.comId}/s/chat/chat-bubble?type=all-my-bubbles&start={start}&size={size}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        return BubbleList(req.json()["chatBubbleList"])

    def select_bubble(self, bubbleId: str, apply: int = 0, chatId: str = None):
        data = {
            "bubbleId": bubbleId,
            "applyToAll": apply,
            "timestamp": int(timestamp() * 1000)
        }
        if chatId: data["threadId"] = chatId
        data = json.dumps(data)
        sig = ndc(data)
        req = requests.post(f"{self._api}/x{self.comId}/s/chat/thread/apply-bubble", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        else:return Json(req.json())

    def delete_chat_bubble(self, bubbleId: str):
        req = requests.delete(url=f"{self._api}/x{self.comId}/s/chat/chat-bubble/{bubbleId}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return Json(req.json())

    def get_chat_bubble_templates(self, start: int = 0, size: int = 25):
        req = requests.get(f"{self._api}/x{self.comId}/s/chat/chat-bubble/templates?start={start}&size={size}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return BubbleTemplates(req.json()["templateList"])

    def upload_custom_bubble(self, templateId: str, bubble: BinaryIO):
        req = requests.post(f"{self._api}/x{self.comId}/s/chat/chat-bubble/templates/{templateId}/generate", headers=self._headers, data=bubble)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return Json(req.json())

    def kick(self, chatId: str, userId: str, allowRejoin: bool = True):
        if not allowRejoin: allowRejoin = 1
        if allowRejoin: allowRejoin = 0
        req = requests.delete(f"{self._api}/x{self.comId}/s/chat/thread/{chatId}/member/{userId}?allowRejoin={allowRejoin}", headers=self.headers)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return Json(req.json())

    def user_active_time(self):
        try:
            data = {"ndcId": self.comId}
            req = requests.post("https://aminoapps.com/api/community/stats/web-user-active-time", json=data, headers=self.web_headers)
            try:
                if req.json()["code"] != 200: return CheckExceptions(req.json())
                else: Json(req.json())
            except: return CheckExceptions(req.json())
            return Json(req.json())
        except: return 0

    def send_active_obj(self, startTime: int = None, endTime: int = None, optInAdsFlags: int = 2147483647, tz: int = -timezone // 1000, timers: list = None, timestamp: int = int(timestamp() * 1000)):
        data = {
            "userActiveTimeChunkList": [
                {
                    "start": startTime,
                    "end": endTime
                }
            ],
            "timestamp": timestamp,
            "optInAdsFlags": optInAdsFlags,
            "timezone": tz
        }
        if timers: data["userActiveTimeChunkList"] = timers

        data = json_minify(json.dumps(data))
        mac = hmac.new(bytes.fromhex("307c3c8cd389e69dc298d951341f88419a8377f4"), data.encode("utf-8"), sha1)
        sig = base64.b64encode(bytes.fromhex("22") + mac.digest()).decode("utf-8")
        req = requests.post(f"{self._api}/x{self.comId}/s/community/stats/user-active-time", headers=headers.Headers(data=data, sig=sig).headers, data=data)
        if req.status_code != 200: return CheckExceptions(req.json())
        else: return req.status_code
