18 votes

Comment (spécifiquement) puis-je lire un fichier chat.db ?

Comme tout le monde qui utilise l'application Messages d'Apple, j'ai un fichier chat.db. J'aimerais lire le fichier mais rien ne semble donner les résultats escomptés.

Voici ce que j'ai essayé jusqu'à présent:

  • L'approche en ligne de commande consistant à se rendre dans ~/Libray/Messages suivi de open chat.db était parmi les meilleures car quelque chose a été trouvé et il y a eu une tentative d'ouverture du fichier. Malheureusement, cela n'a pas fonctionné comme espéré.

    entrer la description de l'image ici

  • Comme j'avais maintenant ouvert le navigateur de base de données pour SQLite, j'ai essayé d'ouvrir le fichier via l'interface graphique de cette application mais comme le dossier est caché, il n'est pas apparu.

    entrer la description de l'image ici

Quelqu'un a-t-il réussi avec cela ? Je ne peux certainement pas être la première personne à avoir essayé et j'ai également lu ailleurs que le schéma pourrait également poser problème.

14voto

user43889 Points 266

Le fichier que vous avez trouvé est une base de données SQLite et vous avez besoin d'une application qui peut lire les bases de données SQLite. Vous avez de nombreuses options - recherchez dans le Mac App Store pour SQLite (ou similaire) ou utilisez la commande Terminal sqlite3.

Mais une base de données SQLite est intimidante pour un utilisateur novice, même si elle est parmi les plus simples des bases de données relationnelles. Une "base de données relationnelle" organise les données dans une ou plusieurs tables (ou "relations") de lignes et de colonnes. La puissance (et la complexité) des bases de données relationnelles réside dans la connexion des données dans deux tables ou plus via des relations.

Pour comprendre une base de données SQLite, vous avez besoin de :

  1. Une application - j'utilise "SQLPro for SQLite" depuis l'Apple App Store. Sa page web est SQLitePro.
  2. Une requête pour extraire les données que vous souhaitez. J'ai "triché" et en ai trouvé une ici Requête SQL pour chat.

Étapes pour lire chat.db :

  1. Faites une copie de la base de données - j'ai copié la mienne sur le Bureau.
  2. Exécutez votre application et ouvrez chat.db.
  3. Choisissez l'onglet de requête.
  4. Entrez cette requête :

Copiez et collez

sélectionnez
 m.rowid
,coalesce(m.cache_roomnames, h.id) ThreadId
,m.is_from_me IsFromMe
,case when m.is_from_me = 1 then m.account
 else h.id end as FromPhoneNumber
,case when m.is_from_me = 0 then m.account
 else coalesce(h2.id, h.id) end as ToPhoneNumber
,m.service Service

/*,datetime(m.date + 978307200, 'unixepoch', 'localtime') as TextDate -- date stored as ticks since 2001-01-01 */
,datetime((m.date / 1000000000) + 978307200, 'unixepoch', 'localtime') as TextDate /* after iOS11 date needs to be / 1000000000 */

,m.text MessageText

,c.display_name RoomName

de
message as m
left join handle as h on m.handle_id = h.rowid
left join chat as c on m.cache_roomnames = c.room_name /* note: chat.room_name is not unique, this may cause one-to-many join */
left join chat_handle_join as ch on c.rowid = ch.chat_id
left join handle as h2 on ch.handle_id = h2.rowid

où
-- try to eliminate duplicates due to non-unique message.cache_roomnames/chat.room_name
(h2.service is null or m.service = h2.service)

ordre par m.date desc;
  1. Exécutez la requête.

Je peux alors voir tous mes messages (j'ai regroupé certaines colonnes pour masquer partiellement mon contenu) :

Base de données de chat

Vous pouvez utiliser différentes applications - j'ai utilisé dbHarbour (gratuit, je pense).

Ou vous pouvez le faire dans Terminal avec quelques commandes incroyablement détaillées et pointilleuses / impitoyables envers même une seule faute de frappe :

gilby@Beth/Users/gilby/Desktop% sqlite3 chat.db
Version SQLite 3.32.3 2020-06-18 14:16:19
Entrez ".help" pour obtenir des indices d'utilisation.
sqlite> .mode csv
sqlite> .output chat.csv
sqlite> sélect <<<< Il suffit de copier et coller la requête ci-dessus
   ...>  m.rowid
   ...> ,coalesce(m.cache_roomnames, h.id) ThreadId
   ...> ,m.is_from_me IsFromMe
   ...> ,case when m.is_from_me = 1 then m.account
   ...>  else h.id end as FromPhoneNumber
   ...> ,case when m.is_from_me = 0 then m.account
   ...>  else coalesce(h2.id, h.id) end as ToPhoneNumber
   ...> ,m.service Service
   ...>
   ...> /*,datetime(m.date + 978307200, 'unixepoch', 'localtime') as TextDate -- date stored as ticks since 2001-01-01 */
   ...> ,datetime((m.date / 1000000000) + 978307200, 'unixepoch', 'localtime') as TextDate /* after iOS11 date needs to be / 1000000000 */
   ...>
   ...> ,m.text MessageText
   ...>
   ...> ,c.display_name RoomName
   ...>
   ...> de
   ...> message as m
   ...> left join handle as h on m.handle_id = h.rowid
   ...> left join chat as c on m.cache_roomnames = c.room_name /* note: chat.room_name is not unique, this may cause one-to-many join */
   ...> left join chat_handle_join as ch on c.rowid = ch.chat_id
   ...> left join handle as h2 on ch.handle_id = h2.rowid
   ...>
   ...> où
   ...> -- try to eliminate duplicates due to non-unique message.cache_roomnames/chat.room_name
   ...> (h2.service is null or m.service = h2.service)
   ...>
   ...> ordre par m.date desc;      <<<< Appuyez sur Entrée
sqlite>

Les commandes mode et output font en sorte que la sortie aille dans un fichier CSV que vous pouvez ouvrir dans un tableur. La requête est exactement la même qu'auparavant.

C'est une plongée assez profonde dans SQLite. Bonne chance.

ÉDITION IMPORTANTE:

Pour iOS16/macOS13, il y a quelques changements dans chat.db pour prendre en charge les nouvelles fonctionnalités de Messages. Bien que le SQL ci-dessus s'exécute correctement, il ne montre pas le texte du message pour les nouveaux messages.

Lire : https://stackoverflow.com/questions/74579463/how-to-decode-message-summary-info-in-macos-messages-chat-db et https://stackoverflow.com/questions/72646565/in-the-new-macos-13-chat-db-where-is-the-text-from-edited-messages-stored qui nous disent que le texte du message est maintenant dans message_summary_info.

Voici une explication du point de vue d'un spécialiste en criminalistique : Ce que les enquêteurs numériques doivent savoir

Pour l'instant, les changements nécessaires sont hors de ma portée de compétences ! Si quelqu'un a une solution, veuillez l'ajouter à cette réponse.

0 votes

Est-ce que tout fonctionne encore pour vous? Cela fonctionnait pour moi auparavant, mais maintenant je remarque que le champ message.text est vide pour les messages que j'envoie, mais est toujours renseigné pour les messages que je reçois de quelqu'un d'autre.

0 votes

@JessePangburn Non ce n'est pas le cas!! Pour moi, tous les nouveaux messages le champ message.text est vide. J'ai ajouté une édition à ma réponse avec ce que j'ai découvert. Aucune solution.

0 votes

Dommage. Je pensais que c'était juste moi! J'avais créé un chatbot pour les arnaqueurs qui m'envoient des SMS, et ce changement l'a complètement perturbé.

2voto

John-Michael Points 1

OU - vous pourriez Effacer tout ce bruit et aller utiliser ceci

https://github.com/ReagentX/imessage-exporter/blob/develop/docs/features.md

Ou déchiffrer la solution du codeur pour gérer les données du flux tapé.

1voto

user514946 Points 155

Le champ attributedBody est stocké dans un format typedstream propriétaire d'Apple, que j'ai trouvé être en fait assez parseable via le package pytypedstream pour Python (utilisez la fonction typedstream.stream.TypedStreamReader).

En supposant que vous utilisez pandas, voici un exemple complet pour récupérer tous les messages de toutes les conversations. Je vous laisse filtrer les données comme bon vous semble.

import os
import os.path
import sqlite3

import pandas as pd
from typedstream.stream import TypedStreamReader

# Le contenu textuel de certains messages est encodé dans une colonne attribuée spéciale attribuéBody
# sur la ligne du message; cette valeur attribuéeBody est au format propriétaire d'Apple
# format typedstream, mais peut être analysé avec le package pytypedstream
# ()
def decode_message_attributedbody(data):
    if not data:
        return None
    for event in TypedStreamReader.from_data(data):
        # Le premier objet bytes est celui que nous voulons
        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"},
        )
        # Décoder toutes les valeurs attribuéesBody et les fusionner dans la colonne 'text'
        messages_df["text"] = messages_df["text"].fillna(
            messages_df["attributedBody"].apply(decode_message_attributedbody)
        )
        print(messages_df["text"])

if __name__ == "__main__":
    main()

0voto

John-Michael Points 1

J'ai pu résoudre / contourner les blancs sur les nouveaux textes de message. Les données sont dans ce champ attributedBody. Vous avez le choix. Ce que j'ai fait, c'est simplement les lire dans DB Browser for SQLite.

J'ai également lu les fichiers sqlite dans ~/Library/AddressBook et compilé une liste de numéros de téléphone et de noms.

Voici ma nouvelle requête SQL pour toi

 m.rowid
,coalesce(m.cache_roomnames, h.id) ThreadId
,a.field2 Personne
,m.is_from_me IsFromMe
,m.text TexteDuMessage
,m.attributedBody attributedBody
,datetime((m.date / 1000000000) + 978307200, 'unixepoch', 'localtime') as DateTexte
,case when m.is_from_me = 1 then m.account
 else h.id end as NumeroEmetteur
,case when m.is_from_me = 0 then m.account
 else coalesce(h2.id, h.id) end as NumeroDestinataire
,m.service Service

,c.display_name NomChat

from
message as m
left join handle as h on m.handle_id = h.rowid
left join chat as c on m.cache_roomnames = c.room_name /* note: chat.room_name n'est pas unique, cela peut causer une jointure un-à-plusieurs */
left join chat_handle_join as ch on c.rowid = ch.chat_id
left join handle as h2 on ch.handle_id = h2.rowid
left join MasterAddressBook_Filtered as a on h.id = a.field1 /* table d'importation à partir de CSV */

where
(h2.service is null or m.service = h2.service)

order by m.date DESC;````

0 votes

Votre autre option est que vous pouvez travailler avec les packages python / nodejs pour les types Apple. Je n'ai pas encore réussi à faire fonctionner le code pour qu'il lise le BLOB dans attributedBody et le décode pour mettre à jour le messageText. Mais j'y travaille;

0 votes

D'accord maintenant, c'est HACKY comme un sac mais ça a marché. Je souhaite pouvoir tout faire en nodejs mais je ne comprends pas comment lire le pipeline pour obtenir les chaînes de caractères.

0 votes

Le script est trop long donc j'ai créé un GIST gist.github.com/johnnyspire/b9cc2a649edfcec97ed0e0b1175d7caa

LesApples.com

LesApples est une communauté de Apple où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres utilisateurs d'appareils Apple, poser vos propres questions ou résoudre celles des autres.

Powered by:

X