Skip to content

Commit

Permalink
feat: contacts store & replace webptools with pillow
Browse files Browse the repository at this point in the history
  • Loading branch information
krypton-byte committed Jan 16, 2024
1 parent e6aabbb commit 211488d
Show file tree
Hide file tree
Showing 19 changed files with 13,619 additions and 2,821 deletions.
2 changes: 2 additions & 0 deletions examples/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ def handler(client: NewClient, message: MessageEv):
client.send_message(
chat, client.set_default_disappearing_timer(timedelta(days=7)).__str__()
)
case "test_contacts":
client.send_message(chat, client.contact.get_all_contacts().__str__())


@client.event(PairStatusEv)
Expand Down
39 changes: 38 additions & 1 deletion neonize/_binder.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,19 @@ def get_bytes(self):
gocode.Upload.restype = Bytes
gocode.DownloadAny.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int]
gocode.DownloadAny.restype = Bytes
gocode.DownloadMediaWithPath.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_char_p]
gocode.DownloadMediaWithPath.argtypes = [
ctypes.c_char_p,
ctypes.c_char_p,
ctypes.c_char_p,
ctypes.c_int,
ctypes.c_char_p,
ctypes.c_int,
ctypes.c_char_p,
ctypes.c_int,
ctypes.c_int,
ctypes.c_int,
ctypes.c_char_p,
]
gocode.DownloadMediaWithPath.restype = Bytes
gocode.GetGroupInfo.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int]
gocode.GetGroupInfo.restype = Bytes
Expand Down Expand Up @@ -375,5 +387,30 @@ def get_bytes(self):
ctypes.c_char_p,
]
gocode.GetMessageForRetry.restype = Bytes
gocode.PutPushName.argtypes = [
ctypes.c_char_p,
ctypes.c_char_p,
ctypes.c_int,
ctypes.c_char_p,
]
gocode.PutPushName.restype = Bytes
gocode.PutContactName.argtypes = [
ctypes.c_char_p,
ctypes.c_char_p,
ctypes.c_int,
ctypes.c_char_p,
ctypes.c_char_p,
]
gocode.PutPushName.restype = ctypes.c_char_p
gocode.PutAllContactNames.argtypes = [
ctypes.c_char_p,
ctypes.c_char_p,
ctypes.c_int,
]
gocode.PutAllContactNames.restype = ctypes.c_char_p
gocode.GetContact.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int]
gocode.GetContact.restype = Bytes
gocode.GetAllContacts.argtypes = [ctypes.c_char_p]
gocode.GetAllContacts.restype = Bytes
else:
gocode: Any = object()
93 changes: 88 additions & 5 deletions neonize/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import typing
from datetime import timedelta
from io import BytesIO
from typing import Optional, Callable, List
from typing import Any, Optional, Callable, List

import magic
from google.protobuf.internal.containers import RepeatedCompositeFieldContainer
Expand All @@ -16,6 +16,7 @@
from .builder import build_edit, build_revoke
from .events import Event
from .exc import (
ContactStoreError,
DownloadError,
ResolveContactQRLinkError,
SendAppStateError,
Expand Down Expand Up @@ -71,6 +72,12 @@
)
from .proto import Neonize_pb2 as neonize_proto
from .proto.Neonize_pb2 import (
Contact,
ContactEntry,
ContactEntryArray,
ContactInfo,
ContactsGetContactReturnFunction,
ContactsPutPushNameReturnFunction,
GroupParticipant,
Blocklist,
GroupLinkTarget,
Expand Down Expand Up @@ -133,6 +140,60 @@
from .utils.thumbnail import generate_thumbnail


class ContactStore:
def __init__(self, uuid: bytes) -> None:
self.uuid = uuid
self.__client = gocode

def put_pushname(
self, user: JID, pushname: str
) -> ContactsPutPushNameReturnFunction:
user_bytes = user.SerializeToString()
model = ContactsPutPushNameReturnFunction.FromString(
self.__client.PutPushName(
user_bytes, len(user_bytes), pushname.encode()
).get_bytes()
)
if model.Error:
raise ContactStoreError(model.Error)
return model

def put_contact_name(self, user: JID, fullname: str, firstname: str):
user_bytes = user.SerializeToString()
err = self.__client.PutContactName(
self.uuid,
user_bytes,
len(user_bytes),
fullname.encode(),
firstname.encode(),
).decode()
if err:
return ContactStoreError(err)

def put_all_contact_name(self, contact_entry: List[ContactEntry]):
entry = ContactEntryArray(ContactEntry=contact_entry).SerializeToString()
err = self.__client.PutAllContactNames(self.uuid, entry, len(entry)).decode()
if err:
raise ContactStoreError(err)

def get_contact(self, user: JID) -> ContactInfo:
jid = user.SerializeToString()
model = ContactsGetContactReturnFunction.FromString(
self.__client.GetContact(self.uuid, jid, len(jid)).get_bytes()
)
if model.Error:
raise ContactStoreError(model.Error)
return model.ContactInfo

def get_all_contacts(self) -> RepeatedCompositeFieldContainer[Contact]:
model = neonize_proto.ContactsGetAllContactsReturnFunction.FromString(
self.__client.GetAllContacts(self.uuid).get_bytes()
)
if model.Error:
raise ContactStoreError(model.Error)
return model.Contact


class NewClient:
def __init__(
self,
Expand All @@ -158,6 +219,7 @@ def __init__(
self.event = Event(self)
self.blocking = self.event.blocking
self.qr = self.event.qr
self.contact = ContactStore(self.uuid)
log.debug("🔨 Creating a NewClient instance")

def __onLoginStatus(self, s: str):
Expand Down Expand Up @@ -721,10 +783,20 @@ def download_any(
else:
return media.Binary
return None
def download_media_with_path(self, direct_path: str, enc_file_hash: bytes, file_hash: bytes, media_key: bytes, file_length: int, media_type: MediaType, mms_type: str) -> bytes:

def download_media_with_path(
self,
direct_path: str,
enc_file_hash: bytes,
file_hash: bytes,
media_key: bytes,
file_length: int,
media_type: MediaType,
mms_type: str,
) -> bytes:
"""
Downloads media with the given parameters and path. The media is downloaded from the path specified.
:param direct_path: The direct path to the media to be downloaded.
:type direct_path: str
:param enc_file_hash: The encrypted hash of the file.
Expand All @@ -742,15 +814,26 @@ def download_media_with_path(self, direct_path: str, enc_file_hash: bytes, file_
:raises DownloadError: If there is an error in the download process.
:return: The downloaded media in bytes.
:rtype: bytes
"""
"""
model = neonize_proto.DownloadReturnFunction.FromString(
self.__client.DownloadMediaWithPath(
self.uuid, direct_path.encode(), enc_file_hash, len(enc_file_hash), file_hash, len(file_hash), media_key, len(media_key), file_length, media_type.value, mms_type.encode()
self.uuid,
direct_path.encode(),
enc_file_hash,
len(enc_file_hash),
file_hash,
len(file_hash),
media_key,
len(media_key),
file_length,
media_type.value,
mms_type.encode(),
).get_bytes()
)
if model.Error:
raise DownloadError(model.Error)
return model.Binary

def generate_message_id(self) -> str:
"""Generates a unique identifier for a message.
Expand Down
4 changes: 4 additions & 0 deletions neonize/exc.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,7 @@ class UpdateGroupParticipantsError(Exception):

class UnsupportedEvent(Exception):
pass


class ContactStoreError(Exception):
pass
34 changes: 32 additions & 2 deletions neonize/goneonize/Neonize.proto
Original file line number Diff line number Diff line change
Expand Up @@ -520,12 +520,42 @@ message PatchInfo {
required WAPatchName Type = 2;
repeated MutationInfo Mutations = 3;
}
message ContactsPutPushNameReturnFunction{
required bool Status = 1;
optional string PreviousName = 2;
optional string Error = 3;
}
message ContactEntry {
required JID JID = 1;
required string FirstName = 2;
required string FullName = 3;
}
message ContactEntryArray {
repeated ContactEntry ContactEntry = 1;
}
message SetPrivacySettingReturnFunction {
optional PrivacySettings settings = 1;
optional string Error = 2;
}


message ContactsGetContactReturnFunction{
optional ContactInfo ContactInfo = 1;
optional string Error = 2;
}
message ContactInfo {
required bool Found = 1;
required string FirstName = 2;
required string FullName = 3;
required string PushName = 4;
required string BusinessName = 5;
}
message Contact{
required JID JID = 1;
required ContactInfo Info = 2;
}
message ContactsGetAllContactsReturnFunction{
repeated Contact Contact = 1;
optional string Error = 2;
}
// events
message QR{ //1
repeated string Codes = 1;
Expand Down
2 changes: 1 addition & 1 deletion neonize/goneonize/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def build_neonize():
print(f"os: {os_name}, arch: {arch_name}")
filename = generated_name(os_name, arch_name)
subprocess.call(
shlex.split(f"go build -buildmode=c-shared -ldflags=-s -o {filename} main.go"),
shlex.split(f"go build -buildmode=c-shared -ldflags=-s -o {filename} "),
cwd=cwd,
env=os.environ.update({"CGO_ENABLED": "1"}),
)
Expand Down
122 changes: 122 additions & 0 deletions neonize/goneonize/contact_store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package main

import (
"C"

"github.com/krypton-byte/neonize/neonize"
"github.com/krypton-byte/neonize/utils"
"google.golang.org/protobuf/proto"
)
import "go.mau.fi/whatsmeow/store"

//export PutPushName
func PutPushName(id *C.char, user *C.uchar, userSize C.int, pushname *C.char) C.struct_BytesReturn {
var userJID neonize.JID
err := proto.Unmarshal(getByteByAddr(user, userSize), &userJID)
if err != nil {
panic(err)
}
return_ := neonize.ContactsPutPushNameReturnFunction{}
status, prev_name, err := clients[C.GoString(id)].Store.Contacts.PutPushName(utils.DecodeJidProto(&userJID), C.GoString(pushname))
return_.PreviousName = proto.String(prev_name)
return_.Status = &status
if err != nil {
return_.Error = proto.String(err.Error())
}
return_bytes, err := proto.Marshal(&return_)
if err != nil {
panic(err)
}
return ReturnBytes(return_bytes)
}

//export PutBusinessName
func PutBusinessName(id *C.char, user *C.uchar, userSize C.int, businessName *C.char) C.struct_BytesReturn {
var userJID neonize.JID
err := proto.Unmarshal(getByteByAddr(user, userSize), &userJID)
if err != nil {
panic(err)
}
return_ := neonize.ContactsPutPushNameReturnFunction{}
status, prev_name, err := clients[C.GoString(id)].Store.Contacts.PutBusinessName(utils.DecodeJidProto(&userJID), C.GoString(businessName))
return_.PreviousName = proto.String(prev_name)
return_.Status = &status
if err != nil {
return_.Error = proto.String(err.Error())
}
return_bytes, err := proto.Marshal(&return_)
if err != nil {
panic(err)
}
return ReturnBytes(return_bytes)
}

//export PutContactName
func PutContactName(id *C.char, user *C.uchar, userSize C.int, fullName, firstName *C.char) *C.char {
var userJID neonize.JID
err := proto.Unmarshal(getByteByAddr(user, userSize), &userJID)
if err != nil {
panic(err)
}
err_ := clients[C.GoString(id)].Store.Contacts.PutContactName(utils.DecodeJidProto(&userJID), C.GoString(fullName), C.GoString(firstName))
if err_ != nil {
return C.CString(err_.Error())
}
return C.CString("")
}

//export PutAllContactNames
func PutAllContactNames(id *C.char, contacts *C.uchar, contactsSize C.int) *C.char {
var entry neonize.ContactEntryArray
err := proto.Unmarshal(getByteByAddr(contacts, contactsSize), &entry)
if err != nil {
panic(err)
}
var contactEntry = make([]store.ContactEntry, len(entry.ContactEntry))
for i, centry := range entry.ContactEntry {
contactEntry[i] = *utils.DecodeContactEntry(centry)
}
err_r := clients[C.GoString(id)].Store.Contacts.PutAllContactNames(contactEntry)
if err_r != nil {
return C.CString(err_r.Error())
}
return C.CString("")
}

//export GetContact
func GetContact(id *C.char, user *C.uchar, userSize C.int) C.struct_BytesReturn {
var userJID neonize.JID
err := proto.Unmarshal(getByteByAddr(user, userSize), &userJID)
if err != nil {
panic(err)
}
contact_info, err_ := clients[C.GoString(id)].Store.Contacts.GetContact(utils.DecodeJidProto(&userJID))
return_ := neonize.ContactsGetContactReturnFunction{
ContactInfo: utils.EncodeContactInfo(contact_info),
}
if err_ != nil {
return_.Error = proto.String(err_.Error())
}
return_bytes, err_proto := proto.Marshal(&return_)
if err_proto != nil {
panic(err_proto)
}
return ReturnBytes(return_bytes)
}

//export GetAllContacts
func GetAllContacts(id *C.char) C.struct_BytesReturn {
contacts, err := clients[C.GoString(id)].Store.Contacts.GetAllContacts()
return_ := neonize.ContactsGetAllContactsReturnFunction{
Contact: utils.EncodeContacts(contacts),
}
if err != nil {
return_.Error = proto.String(err.Error())
}
return_bytes, err_proto := proto.Marshal(&return_)
if err_proto != nil {
panic(err_proto)
}
return ReturnBytes(return_bytes)

}
Loading

0 comments on commit 211488d

Please sign in to comment.