├── LICENSE ├── README.md └── main.py /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Run `main.py` on your Mac to text with Open Interpreter. 2 | 3 | Credit to the incredible [@morisy](https://github.com/morisy). 4 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sqlite3 3 | import time 4 | import interpreter 5 | import subprocess 6 | import signal 7 | import sys 8 | 9 | database_path = f'/Users/{os.environ.get("LOGNAME") or os.environ.get("USER")}/Library/Messages/chat.db' 10 | seen_messages = [] 11 | verbose_mode = False 12 | send_message_in_paragraphs = True 13 | 14 | def verbose_print(message): 15 | if verbose_mode: 16 | print(message) 17 | 18 | def get_last_five_contacts(): 19 | verbose_print("Getting the last five contacts...") 20 | conn = sqlite3.connect(database_path) 21 | curs = conn.cursor() 22 | 23 | # Query to get the last 20 recent contacts 24 | curs.execute(""" 25 | SELECT DISTINCT id 26 | FROM handle 27 | JOIN message ON handle.ROWID = message.handle_id 28 | ORDER BY message.date DESC 29 | LIMIT 20; 30 | """) 31 | 32 | contacts = [item[0] for item in curs.fetchall()] 33 | conn.close() 34 | 35 | # Filtering contacts using get_latest_imessage_from_contact 36 | valid_contacts = [] 37 | for contact in contacts: 38 | verbose_print(f"Checking if contact {contact} has a valid iMessage...") 39 | if get_latest_imessage_from_contact(contact) is not None: 40 | valid_contacts.append(contact) 41 | if len(valid_contacts) == 5: 42 | break 43 | 44 | return valid_contacts 45 | 46 | def get_latest_imessage_from_contact(contact): 47 | verbose_print(f"Getting the latest iMessage from contact {contact}...") 48 | conn = sqlite3.connect(database_path) 49 | curs = conn.cursor() 50 | curs.execute("SELECT ROWID FROM handle WHERE id=?", (contact,)) 51 | handle_id = curs.fetchone() 52 | if not handle_id: 53 | return None 54 | 55 | curs.execute("SELECT text FROM message WHERE handle_id=? ORDER BY date DESC LIMIT 1;", (handle_id[0],)) 56 | message_data = curs.fetchone() 57 | last_rowid = None 58 | 59 | # If message_data is None or is an empty string, try the next older message 60 | while not message_data or not message_data[0].strip(): 61 | curs.execute("SELECT text, ROWID FROM message WHERE handle_id=? AND (ROWID {latest_message}\n") 131 | seen_messages.append(latest_message) 132 | process_message(contact, latest_message) 133 | print("\n(Listening)") 134 | 135 | time.sleep(4) 136 | 137 | def signal_handler(sig, frame): 138 | print('\n\nExiting...\n') 139 | sys.exit(0) 140 | 141 | signal.signal(signal.SIGINT, signal_handler) 142 | 143 | def main(): 144 | print("\n●\n\nWelcome to The Open Interpreter iMessage Server.\n\nPress CTRL-C to exit.\n") 145 | time.sleep(1) 146 | 147 | contacts = get_last_five_contacts() 148 | print("Last 5 iMessage contacts:\n\n" + '\n'.join(f"- {contact}" for contact in contacts)) 149 | 150 | input_contacts = input("\nEnter trusted contacts to control Open Interpreter, separated by commas:\n\n") 151 | selected_contacts = input_contacts.split(',') 152 | selected_contacts = [contact.strip() for contact in selected_contacts] 153 | 154 | print(f"\n\n● Now listening for messages from: {selected_contacts} ...\n") 155 | poll_for_messages(selected_contacts) 156 | 157 | if __name__ == "__main__": 158 | main() 159 | --------------------------------------------------------------------------------