Skip to content

Commit

Permalink
0.7.12
Browse files Browse the repository at this point in the history
With Sonoma apple decided the iMsg database would store the message data in a Apple NSString Archived object.  Taking up oodles more room.
It appears the plain text is quickly converted and then becomes NULL.
Update plugin to decode message.attributedbody using typedstream and decoded contents of NSString formatting.
Testing...

0.7.10
Move model to gpt-3.5-16k
Increase token count to 8000
TODO user selectable both above when time

0.7.9
Model back to gpt-3.5
& remove some while loops trying to track down occasional 'hang'

0.7.8
Change model to gpt-4 when rolls out for all.

0.7.7
Update openai library to 0.27.4

0.7.6
Add chatGPT only Buddy - these individuals can only converse with chatGPT.  They won't have any trigger or command options
If device control enabled will allow device Control, but increasingly not sure this is a great idea for this plugin

0.7.5
Handle and specifically message regarding timeout, ratelimt openai errors

0.7.4
OpenAI reports that the system prompt is largely ignored with user prompt having more 'attention'
Because of this duplicate some setup into a new user prompt

0.7.3
Fix for debugextra logging string conversion issue (also in 0.7.2)
Better checking for token usage and deletion of prompts before gets to limit
Add 2nd Pluginconfig Personal info:  Aim of this is to educate chatGPT as to who the various users/buddies are.
Should be Buddy Handle followed by | and then description written in first person.
buddyhandle|I am user Glenn.I am.. |buddyhandle2|I am user Glenns co-worker.

Probably would better be a setup file, as really need to type somewhere else and paste in...

## 0.7.1
Fix for quotes

## New 0.7.0

Add support for ChatGPT 3.5 turbo API usage.(Beta)
This can be used to control indigo devices (so marked for control), like wit.ai - however it probably needs a bit of maturing before that works 100%

Currently though the chatbot, chat, information, advice function via chatGPT works very well and enables easy access to chatGPT replies for whatever usage.
Like chatGPT warning - accuracy here depends on the subject, but for natural language processing it is great.
  • Loading branch information
Ghawken committed Nov 18, 2023
1 parent e869d7a commit 10e4976
Show file tree
Hide file tree
Showing 15 changed files with 4,622 additions and 7 deletions.
2 changes: 1 addition & 1 deletion iMessage.indigoPlugin/Contents/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>PluginVersion</key>
<string>0.7.11</string>
<string>0.7.12</string>
<key>ServerApiVersion</key>
<string>3.0.0</string>
<key>IwsApiVersion</key>
Expand Down
36 changes: 30 additions & 6 deletions iMessage.indigoPlugin/Contents/Server Plugin/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import re
import random
import datetime
import typedstream

try:
import indigo
Expand Down Expand Up @@ -448,7 +449,7 @@ def sql_fetchmessages(self):
# if self.debugextra:
# self.debugLog(u"fetch messages() method called.")
cursor = self.connection.cursor()

using_attributedBody = False
#below is needed for older than Mojave
if self.systemVersion >=22:
sqlcommand = '''
Expand All @@ -459,6 +460,7 @@ def sql_fetchmessages(self):
datetime(message.date/1000000000 + strftime("%s", "2001-01-01") ,"unixepoch","localtime") >= datetime('now','-10 seconds', 'localtime')
ORDER BY message.date ASC;
'''
using_attributedBody = True
elif self.systemVersion >=17:
sqlcommand = '''
SELECT handle.id, message.text, message.is_audio_message
Expand Down Expand Up @@ -496,16 +498,38 @@ def sql_fetchmessages(self):
return dict()
else:
if self.debugextra:
self.logger.debug(u'sql_fetchmessages: Not empty return:' + str(result))

self.logger.debug(u'sql_fetchmessages: Not empty return:\n' + str(result))
self.logger.debug(f"Type result {type(result)}")
newlist = []
for items in result:
result_list = map(list, result)
if self.debugextra:
self.logger.debug(u'Convert to List of Lists\n' + str(result_list))

for items in result_list:
## Given Ventura and Sonoma now uses messsage.attributedBody to save text as a Archive NSString.
## Yikes.
if items[2]==1:
self.logger.debug(u'Must be audio file...')
newtuple = items[0], 'AUDIOFILE'
newtuple = [items[0], 'AUDIOFILE']
newlist.append(newtuple)
else:
newtuple = items[0], items[1]
if using_attributedBody:
## Need to read and convert the Hex NSString archived object back to Text
try:
bytes_data = bytes.fromhex(items[1])
self.logger.debug(f"Using Attributed Body iMsg - thank you Apple.")
self.logger.debug(f"Converted Bytes_data {bytes_data}")
data = typedstream.unarchive_from_data(bytes_data)
if self.debugextra:
self.logger.debug(f"Extracted NSString archive: \n {data.contents}")
self.logger.debug(f"Message: == {data.contents[0].value.value}")
message = data.contents[0].value.value
items[1] = str(message)
except:
self.logger.exception("Exception unpacked NSString message.attributedbody")
pass

newtuple = [items[0], items[1]]
newlist.append(newtuple)

self.logger.debug(u'newlist after checking audio file:'+str(newlist))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# This file is part of the python-typedstream library.
# Copyright (C) 2020 dgelessus
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.


# "Unused" imports and star imports are ok in __init__.py.
from .stream import InvalidTypedStreamError # noqa: F401
from .archiving import * # noqa: F401, F403

# .types and its submodules are normally not used directly,
# but it's important that they are imported,
# so that their classes are registered with KnownArchivedObject and KnownStruct.
from . import types # noqa: F401

# To release a new version:
# * Remove the .dev suffix from the version number in this file.
# * Update the changelog in the README.md (rename the "next version" section to the correct version number).
# * Remove the ``dist`` directory (if it exists) to clean up any old release files.
# * Run ``python3 setup.py sdist bdist_wheel`` to build the release files.
# * Run ``python3 -m twine check dist/*`` to check the release files.
# * Fix any errors reported by the build and/or check steps.
# * Commit the changes to main.
# * Tag the release commit with the version number, prefixed with a "v" (e. g. version 1.2.3 is tagged as v1.2.3).
# * Fast-forward the release branch to the new release commit.
# * Push the main and release branches.
# * Upload the release files to PyPI using ``python3 -m twine upload dist/*``.
# * On the GitHub repo's Releases page, edit the new release tag and add the relevant changelog section from the README.md.

# After releasing:
# * (optional) Remove the build and dist directories from the previous release as they are no longer needed.
# * Bump the version number in this file to the next version and add a .dev suffix.
# * Add a new empty section for the next version to the README.md changelog.
# * Commit and push the changes to main.

__version__ = "0.1.1.dev"
180 changes: 180 additions & 0 deletions iMessage.indigoPlugin/Contents/Server Plugin/typedstream/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# This file is part of the python-typedstream library.
# Copyright (C) 2020 dgelessus
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.


import argparse
import sys
import typing


from . import __version__
from . import advanced_repr
from . import archiving
from . import stream


def make_subcommand_parser(subs: typing.Any, name: str, *, help: str, description: str, **kwargs: typing.Any) -> argparse.ArgumentParser:
"""Add a subcommand parser with some slightly modified defaults to a subcommand set.
This function is used to ensure that all subcommands use the same base configuration for their ArgumentParser.
"""

ap = subs.add_parser(
name,
formatter_class=argparse.RawDescriptionHelpFormatter,
help=help,
description=description,
allow_abbrev=False,
add_help=False,
**kwargs,
)

ap.add_argument("--help", action="help", help="Display this help message and exit.")

return ap


def open_typedstream_file(file: str) -> stream.TypedStreamReader:
if file == "-":
return stream.TypedStreamReader(sys.stdin.buffer)
else:
return stream.TypedStreamReader.open(file)


def dump_typedstream(ts: stream.TypedStreamReader) -> typing.Iterable[str]:
yield f"streamer version {ts.streamer_version}, byte order {ts.byte_order}, system version {ts.system_version}"
yield ""
indent = 0
next_object_number = 0
for event in ts:
if isinstance(event, (stream.EndTypedValues, stream.EndObject, stream.EndArray, stream.EndStruct)):
indent -= 1

rep = ("\t" * indent) + str(event)
if isinstance(event, (stream.CString, stream.SingleClass, stream.BeginObject)):
rep += f" (#{next_object_number})"
next_object_number += 1
yield rep

if isinstance(event, (stream.BeginTypedValues, stream.BeginObject, stream.BeginArray, stream.BeginStruct)):
indent += 1


def do_read(ns: argparse.Namespace) -> typing.NoReturn:
with open_typedstream_file(ns.file) as ts:
for line in dump_typedstream(ts):
print(line)

sys.exit(0)


def dump_decoded_typedstream(ts: stream.TypedStreamReader) -> typing.Iterable[str]:
unarchiver = archiving.Unarchiver(ts)
for obj in unarchiver.decode_all():
yield from advanced_repr.as_multiline_string(obj)


def do_decode(ns: argparse.Namespace) -> typing.NoReturn:
with open_typedstream_file(ns.file) as ts:
for line in dump_decoded_typedstream(ts):
print(line)

sys.exit(0)


def main() -> typing.NoReturn:
"""Main function of the CLI.
This function is a valid setuptools entry point.
Arguments are passed in sys.argv,
and every execution path ends with a sys.exit call.
(setuptools entry points are also permitted to return an integer,
which will be treated as an exit code.
We do not use this feature and instead always call sys.exit ourselves.)
"""

ap = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""
%(prog)s is a tool for dumping typedstream files, which are produced by
the NSArchiver class in Apple's Foundation framework, as well as the
NXTypedStream APIs in the older NeXTSTEP OS.
""",
allow_abbrev=False,
add_help=False,
)

ap.add_argument("--help", action="help", help="Display this help message and exit.")
ap.add_argument("--version", action="version", version=__version__, help="Display version information and exit.")

subs = ap.add_subparsers(
dest="subcommand",
metavar="SUBCOMMAND",
)

sub_read = make_subcommand_parser(
subs,
"read",
help="Read and display the raw contents of a typedstream.",
description="""
Read and display the raw contents of a typedstream.
All information is displayed as it's stored in the typedstream and is processed
as little as possible. In particular, object references are not resolved
(although each object's reference number is displayed, so that the references
can be followed manually), and objects aren't handled differently based on
their class.
""",
)
sub_read.add_argument("file", help="The typedstream file to read, or - for stdin.")

sub_decode = make_subcommand_parser(
subs,
"decode",
help="Read, decode and display the contents of a typedstream.",
description="""
Read, decode and display the contents of a typedstream.
Where possible, the data read from the typedstream is decoded into a
higher-level structure before being displayed. Objects are decoded based on
their class when their format is known and implemented. Objects of unknown
classes are also supported, but are decoded to a generic format based on the
typedstream data.
As a result of this decoding, some low-level information from the typedstream
is discarded and not displayed, such as raw type encoding strings in known
classes, and object reference numbers. To see this low-level information,
use the read subcommand instead.
""",
)
sub_decode.add_argument("file", help="The typedstream file to read, or - for stdin.")

ns = ap.parse_args()

if ns.subcommand is None:
print("Missing subcommand", file=sys.stderr)
sys.exit(2)
elif ns.subcommand == "read":
do_read(ns)
elif ns.subcommand == "decode":
do_decode(ns)
else:
print(f"Unknown subcommand: {ns.subcommand!r}", file=sys.stderr)
sys.exit(2)


if __name__ == "__main__":
sys.exit(main())
Loading

0 comments on commit 10e4976

Please sign in to comment.