Skip to main content
caleb531 u/caleb531 avatar

caleb531

u/caleb531

Feed options
Hot
New
Top
View
Card
Compact

MAJOR UPDATE: I have a solution! And it seems to work very well!

TL;DR — install the pytypedstream package and use TypedStreamReader to read the first event that is of type bytes (the code snippet below assumes you are using the pandas library for Python):

import pandas as pd
from typedstream.stream import TypedStreamReader

messages_df = pd.read_sql_query(...)

# the real magic
for data in messages_df["attributedBody"]:
    if not data:
        return None
    for event in TypedStreamReader.from_data(data):
        if type(event) is bytes:
            print(event.decode("utf-8"))

Long Answer:

The attributedBody field is stored in an Apple-proprietary typedstream format, which I've found is actually quite parseable via the pytypedstream package for Python (use the typedstream.stream.TypedStreamReader function).

Assuming you are using pandas, here is a full example to retrieve all messages across all conversations. I leave it to you to filter the data how you please.

import os
import os.path
import sqlite3

import pandas as pd
from typedstream.stream import TypedStreamReader


# The textual contents of some messages are encoded in a special attributedBody
# column on the message row; this attributedBody value is in Apple's proprietary
# typedstream format, but can be parsed with the pytypedstream package
# (<https://pypi.org/project/pytypedstream/>)
def decode_message_attributedbody(data):
    if not data:
        return None
    for event in TypedStreamReader.from_data(data):
        # The first bytes object is the one we want
        if type(event) is bytes:
            return event.decode("utf-8")


def main():
    db_path = os.path.expanduser("~/Library/Messages/chat.db")
    with sqlite3.connect(db_path) as connection:
        messages_df = pd.read_sql_query(
            sql="SELECT text, attributedBody FROM message ORDER BY date DESC",
            con=connection,
            parse_dates={"datetime": "ISO8601"},
        )
        # Decode any attributedBody values and merge them into the 'text' column
        messages_df["text"] = messages_df["text"].fillna(
            messages_df["attributedBody"].apply(decode_message_attributedbody)
        )
        print(messages_df["text"])


if __name__ == "__main__":
    main()

Yes, I am able to view the actual text message buried inside the binary contents of chat.db (via the `cat` or `less` commands, the latter is recommended because it's a TON of output).

How did I pick one? By examining the database, I figured out where the messages started appearing as non-NULL, then I picked some message I sent before that period (which turned out to be NULL). Ideally, you should pick a message with an short, unambiguous phrase that is easy to search (e.g. where no other message contains that phrase).


u/False_Dog_3660 Yep, I am on Ventura (macOS 13).


I would just like to add that I am currently experiencing this same issue. There are a large number of messages which have records in the chat.db database (under the "message" table), but have a value of NULL for the "text" column. This is the case whether I use sqlite3 from the Terminal directly, or use an app like DB Browser for SQLite.

Interestingly, if I pick one of these messages, the text value is actually buried in the binary contents of the file:

less -f ~/Library/Messages/chat.db
# type / (slash) and type some keywords in the message you're looking for
# press Enter to search

I have not yet figured out why these values are NULL, but I thought I would post my current findings here.


I'm no musician, but hearing how the score utilized the circle of fifths fascinates me. What completely blew me away, though, is what I read on the Wikipedia page for the circle of fifths:

"In music theory, the circle of fifths (or circle of fourths) is the relationship among the 12 tones of the chromatic scale..."

I stopped in amazement after I read the number 12. From this, I can only conclude that the 12 locations (as well as the 1/12 ratio) are based on the 12 tones of the chromatic scale. That would make perfect sense in light of u/nu7kevin's "circle of fifths" observation.

It's only a theory, but I want to believe it's true... XD


u/lizsa Yes, it is surely possible to beat the AI. The GitHub page explains the details, but essentially, the AI looks for rows of three chips and an empty slot (in any order). You can exploit this strategy to win every time (though usually through a similar sequence of moves).


My Keybase proof [reddit:caleb531 = keybase:caleb531] (Aph-FUihYILOnffqPpUHfDlqnwAGoH2Ffk0UprOLxNw)
Image
r/KeybaseProofs
My Keybase proof [reddit:caleb531 = keybase:caleb531] (Aph-FUihYILOnffqPpUHfDlqnwAGoH2Ffk0UprOLxNw)

Keybase proof

I hereby claim:

  • I am caleb531 on reddit.

  • I am caleb531 on keybase.

  • I have a public key whose fingerprint is E2C3 6FD6 A00F 371F DBFD ED58 A324 8101 38AF D374

To claim this, I am signing this object:

{
    "body": {
        "key": {
            "eldest_kid": "012008eb1b0766f19b59fec8ec1d31edb1ec0c444d0c1ac36759cd597d9b4061ebd80a",
            "fingerprint": "e2c36fd6a00f371fdbfded58a324810138afd374",
            "host": "keybase.io",
            "key_id": "a324810138afd374",
            "kid": "0101d59d738afc5e01529327864d0121393fa2f958ff64ad172d5c311fb99e408f2c0a",
            "uid": "693168e9f64bacff6a21093e1290c619",
            "username": "caleb531"
        },
        "service": {
            "name": "reddit",
            "username": "caleb531"
        },
        "type": "web_service_binding",
        "version": 1
    },
    "ctime": 1486185411,
    "expire_in": 157680000,
    "prev": "26157b4256b259edaab7a45e88409f3bc2810567a421970297952ac3482129dd",
    "seqno": 17,
    "tag": "signature"
}

with the key from above, yielding:

-----BEGIN PGP MESSAGE-----
Version: Keybase OpenPGP v2.0.62
Comment: https://keybase.io/crypto

yMNaAnicdVJtUFRVGN612JKJcogvZ2eguTlNBcg99/sCJYqyRAwz7uSII7nde8+5
yw3cXXaXJQIKwVGBYgwzrRGUCtLGDWwmvocGtYixCciRBLJyaYmE8GsgA5TOMvqv
zp8z53mf55nnfc97PuQhXbD+k3V79ELLRbf+Qt/BQl32YeVcCSHbYTGRWELkoZUL
5UPkclvyNEgkEiSgSFJAMpBJnuNUIMqsqCJFQAqANEBQBkghFYZhIKkASaE5nhUV
yIo8FGWG5ACSoUBKRByhajYrcjqcms2NbRGFqSrkJJJUaR6oUFYhgqwg0RQjABLQ
gqRCmmewMNfuCihwOFlyofWaHWP4YVmJ9x/8B7lJgGNAPlBRWEQClhJpihc4nBRQ
gBZpVaJUkRVUlWMkCHgKsgoNgCqLImJIQaWUldyFK3acSANOQCLmypKCJRIFSJFG
gBJJhQNigOhCTpu0G2G2IuUjmaUBURZHYNSjKSgw2PtVJ4JQc/+/wl3sCEBFSLbc
F1tkzQbx/LDGg5wuzW4jEgFmKm4toAaMwAGBZQCII9AbDs2JLFqAwfKcQOITRzic
yIMtKQ5jMkOxnEyxIoKSJPMSwyJBYEhRpWWFwrNkOYxRQORJSuRFlsKfyggUbhRC
ItBOgc2OvXmcU7JiT5dmtUnuQiciys725Tys0wfrDEGrAoulC1695sG6GY88cTfq
i0/H6ou7ibw240jGXF3ZzFxTf/zpjK8jNxSSWQmnP3wq/vLi6LWStPzo4MfM5m/H
5ZJ1ri1Vh60fJbX5JwZHvQnwmUZi/QtjA23VSvNv9B8R74XsyLnbczDc0l+VLrpM
IxNnbs9aN5f/nTKe3fLa1XT7lZYjlWk1WS9xW3OjRzyz1fNdR8H0cvfi7z3J+x1G
y4X4imt7gxREnVjevrV05MCNmTGiLjJko36hquO70kuxhlVmk+/1R579545v+NR1
/16f58fkt/hTSaVee2xHyrDRENbR9GVRxeS7d5R8r7kwFUTsbt6ZMd13Na23f6il
cs/JjIhbQe5D+9J/WnWiNXK2Yi70yoak6IIoYWlT3+Ch5l0Lk1mfHwhpGEpqCw0v
fu7ipQ+6a1dXrj3WMO8P37Ql8k8wsZA0lhz9jTH5+IuxOTE/h25XbjRsg2tCU2M+
DqsZZBLp4/vm21OnTTUDjv7N6e9MeM+0Nnqr6s9NedradSXBRVFlG9N6c+y/VHZ9
9aglrsrU+8pnv9b5y0zDy/qe67eHd7SHv5pi9L15VCdMST9M5gZVjl5un3Y3pQwM
5oeUz7y/1uLzFRWcN9zMHA9tP5u5bYncZd7fd8+gdtWb+FvZ5QPDMWH+To814fud
kebHW59cXPjL0NnZWF1rf76pc+nlhpM3s7y1jYPuqafvDdUce9uf+S9bm8RO
=5ySU
-----END PGP MESSAGE-----

Finally, I am proving my reddit account by posting it in r/KeybaseProofs