PURSUE ULTIMATE FACTS

2023-03-13
ChatGPT Browser Automation

In [ ]:
%%bash
cd ~ && git clone https://gitlab.com/awesomeai/awesome-chatgpt-zh.git
In [ ]:
%%writefile ~/go-chatgpt-api.yaml
services:
  go-chatgpt-api:
    container_name: go-chatgpt-api
    image: linweiyuan/go-chatgpt-api
    ports:
      - 8080:8080
    environment:
      - GIN_MODE=release
      - GO_CHATGPT_API_PROXY=socks5://chatgpt-proxy-server-warp:65535
    depends_on:
      - chatgpt-proxy-server-warp
    restart: unless-stopped

  chatgpt-proxy-server-warp:
    container_name: chatgpt-proxy-server-warp
    image: linweiyuan/chatgpt-proxy-server-warp
    environment:
      - LOG_LEVEL=OFF
    restart: unless-stopped
In [ ]:
%%bash
docker-compose -f ~/go-chatgpt-api.yaml pull
In [ ]:
%%bash
docker-compose -f ~/go-chatgpt-api.yaml up -d
In [ ]:
%%bash
docker-compose -f ~/go-chatgpt-api.yaml stop
In [ ]:
%%bash
docker-compose -f ~/go-chatgpt-api.yaml start
In [ ]:
%%bash
docker images
REPOSITORY                             TAG       IMAGE ID       CREATED        SIZE
linweiyuan/go-chatgpt-api              latest    81b5896388c5   10 hours ago   18.7MB
linweiyuan/chatgpt-proxy-server-warp   latest    4bf825382c06   2 weeks ago    471MB
In [ ]:
%%bash
docker ps
In [ ]:
%%bash
docker logs -f go-chatgpt-api

💡 get access_token => accessToken

%%bash
curl -H 'Authorization: <access_token>' http://127.0.0.1:8080/conversations\?offset=0\&limit=1
{"items":[{"id":"...","title":"Jupyter User Requests Help.","create_time":"2023-04-17T10:22:31.256627+00:00","update_time":"2023-04-17T10:22:33+00:00"}],"total":11,"limit":1,"offset":0,"has_missing_conversations":false}
In [ ]:
class Common:
    chat_gpt_model = 'gpt-3.5-turbo'
    role_user = 'user'
    role_assistant = 'assistant'

    question_answer_map = {}
    message_channel = []
    exit_for_loop_channel = []
    response_text_channel = []
    conversation_done_channel = []
    parent_message_id = ''
    conversation_id = ''
    reload_conversations_channel = []
    current_node = None
In [ ]:
import json, os, requests, uuid


chat_gpt_base_url = 'http://127.0.0.1:8080'

# open the JSON file and read the access_token
with open(os.path.expanduser('~/.config/revChatGPT/config.json'), 'r') as f:
    access_token = json.load(f).get('access_token', None)

common = Common()


def get_conversations():
    response = requests.get(f'{chat_gpt_base_url}/conversations?offset=0&limit=100', headers = {'Authorization': access_token})
    return response.json()

def get_conversation(conversation_id):
    response = requests.get(f'{chat_gpt_base_url}/conversation/{conversation_id}', headers = {'Authorization': access_token})
    conversation = response.json()
    current_node = conversation['current_node']
    common.parent_message_id = current_node
    handle_conversation_detail(current_node, conversation['mapping'])
    common.exit_for_loop_channel.append(True)

def handle_conversation_detail(current_node, mapping):
    conversation_detail = mapping[current_node]
    parent_id = conversation_detail.get('parent', '')
    if parent_id != '':
        common.question_answer_map[parent_id] = conversation_detail['message']['content']['parts'][0].strip()
        handle_conversation_detail(parent_id, mapping)
    if 'message' not in conversation_detail:
        return
    message = conversation_detail['message']
    parts = message['content']['parts']
    if len(parts) > 0 and parts[0] != '':
        if message['author']['role'] == common.role_user:
            common.message_channel.append(message)

temp_conversation_id = ''

def start_conversation(content):
    parent_message_id = common.parent_message_id
    if parent_message_id == '' or common.conversation_id == '':
        common.conversation_id = ''
        parent_message_id = str(uuid.uuid4())
    response = requests.post(
        f'{chat_gpt_base_url}/conversation',
        headers = {
            'Authorization': access_token,
            'Content-Type': 'application/json',
            'Accept': 'text/event-stream'
        },
        data = json.dumps({
            'action': 'next',
            'messages': [{
                'id': uuid.uuid4().hex,
                'author': {
                    'role': common.role_user
                },
                'role': common.role_user,
                'content': {
                    'content_type': 'text',
                    'parts': [content]
                }
            }],
            'parent_message_id': parent_message_id,
            'model': common.chat_gpt_model,
            'conversation_id': common.conversation_id,
            'continue_text': 'continue'
        }),
        stream=True
    )

    # get it again from response
    common.parent_message_id = ''

    for line in response.iter_lines():
        if not line.startswith(b'data: '):
            continue

        if line.endswith(b'[DONE]'):
            common.conversation_done_channel.append(True)
            break

        make_conversation_response = json.loads(line.decode('utf-8')[len('data: '):])
        if common.parent_message_id == '':
            common.parent_message_id = make_conversation_response['message']['id']
        global temp_conversation_id
        if common.conversation_id == '' and temp_conversation_id == '':
            temp_conversation_id = make_conversation_response['conversation_id']
        if make_conversation_response is not None:
            parts = make_conversation_response['message']['content']['parts']
            if len(parts) > 0:
                common.response_text_channel.append(parts[0])
                yield parts[0]
            if make_conversation_response['message']['end_turn'] == True:
                common.conversation_done_channel.append(True)
                break

    if common.conversation_id == '':
        generate_title(temp_conversation_id)
    else:
        common.reload_conversations_channel.append(True)

def generate_title(conversation_id):
    requests.post(
        f'{chat_gpt_base_url}/conversation/gen_title/{conversation_id}',
        headers = {
            'Authorization': access_token,
            'Content-Type': 'application/json'
        },
        data = json.dumps({
            'message_id': common.parent_message_id,
            'model': common.chat_gpt_model
        })
    )

def rename_title(conversation_id, title):
    requests.patch(
        f'{chat_gpt_base_url}/conversation/{conversation_id}',
        headers={
            'Authorization': access_token,
            'Content-Type': 'application/json'
        },
        data = json.dumps({
            'title': title
        })
    )

def delete_conversation(conversation_id):
    requests.patch(
        f'{chat_gpt_base_url}/conversation/{conversation_id}',
        headers={
            'Authorization': access_token,
            'Content-Type': 'application/json'
        },
        data=json.dumps({
            'is_visible': False
        })
    )

def recover_conversation(conversation_id):
    requests.patch(
        f'{chat_gpt_base_url}/conversation/{conversation_id}',
        headers={
            'Authorization': access_token,
            'Content-Type': 'application/json'
        },
        data=json.dumps({
            'is_visible': True
        })
    )

def clear_conversations():
    requests.patch(f'{chat_gpt_base_url}/conversations', headers = {'Authorization': access_token}, data = {'is_visible': False})

    common.conversation_id = ''
    common.current_node = None
    common.reload_conversations_channel.append(True)
In [ ]:
get_conversations()
In [ ]:
# open the JSON file and read the conversation_id
with open(os.path.expanduser('~/.config/revChatGPT/config.json'), 'r') as f:
    conversation_id = json.load(f).get('conversation_id', None)
In [ ]:
try:
    common.conversation_id = conversation_id
    get_conversation(common.conversation_id)
except RecursionError as errr:
    print('Error Recursion:', errr)
In [ ]:
generate_title(common.conversation_id)
In [ ]:
import IPython
In [ ]:
for response in start_conversation('小 G ,我们又见面了。'):
    IPython.display.display(IPython.core.display.Markdown(response))
    IPython.display.clear_output(wait=True)

是的,有什么我可以帮您的吗?

In [ ]:
%%bash
pip install --upgrade revChatGPT

In [ ]:
%%bash
mkdir -p ~/.config/revChatGPT

💡 get <access_token> => accessToken

In [ ]:
%%writefile ~/.config/revChatGPT/config.json
{
    "access_token": "<access_token>",
    "conversation_id": "<conversation_id>"
}

In [ ]:
import json, os
In [ ]:
from revChatGPT.V1 import Chatbot, configure
In [ ]:
# open the JSON file and read the conversation_id
with open(os.path.expanduser('~/.config/revChatGPT/config.json'), 'r') as f:
    conversation_id = json.load(f).get('conversation_id', None)

In [ ]:
import IPython
In [ ]:
bot = Chatbot(
    config = configure(),
    conversation_id = conversation_id,
    lazy_loading = True,
    base_url = 'http://127.0.0.1:8080/'
)

def ask(prompt):
    for response in bot.ask(prompt):
        IPython.display.display(IPython.core.display.Markdown(response['message']))
        IPython.display.clear_output(wait=True)
In [ ]:
ask('''
小 G ,我们又见面了。
''')

是啊,有什么我能帮助你的吗?


In [ ]:
def gpt_after(parent_id=None):
    return Chatbot(
        config = configure(),
        conversation_id = conversation_id,
        parent_id = parent_id
    )
In [ ]:
for response in gpt_after().ask(
    '女孩子月经期间该怎么护理?'
):
    IPython.display.display(IPython.core.display.Markdown(response['message']))
    IPython.display.clear_output(wait=True)
In [ ]:
for response in gpt_after(response['parent_id']).ask(
    '女孩子月经期间该怎么陪伴?'
):
    IPython.display.display(IPython.core.display.Markdown(response['message']))
    IPython.display.clear_output(wait=True)
In [ ]:
print(f"parent_id: {response['parent_id']}")
In [ ]:
%%bash
npm install -g playwright
In [ ]:
%%bash
npx playwright install webkit
In [ ]:
%%bash
pip install playwright
In [ ]:
%%bash
npx playwright codegen --browser webkit twitter.com

selenium safari

In [ ]:
from selenium import webdriver
driver = webdriver.Safari()

selenium undetected chrome

In [ ]:
import undetected_chromedriver as uc

options = uc.ChromeOptions()
# options.add_argument('--window-size=1024,768')
options.add_argument('--headless')
driver = uc.Chrome(options = options)

driver.get('https://chat.openai.com/auth/login')
In [ ]:
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.ui import WebDriverWait
In [ ]:
WebDriverWait(driver, 5).until(
    expected_conditions.presence_of_element_located((By.XPATH, '//*[text()="Log in"]'))
)

driver.execute_script('''
document.evaluate(
  '//*[text()="Log in"]',
  document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
).snapshotItem(0).dispatchEvent(
  new MouseEvent('click', {
    view: window,
    bubbles: true,
    cancelable: true
  })
);
''')
In [ ]:
WebDriverWait(driver, 5).until(
    expected_conditions.presence_of_element_located((By.XPATH, '//button[@data-provider="google"]'))
)

driver.execute_script('''
document.evaluate(
  '//button[@data-provider="google"]',
  document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
).snapshotItem(0).dispatchEvent(
  new MouseEvent('click', {
    view: window,
    bubbles: true,
    cancelable: true
  })
);
''')
In [ ]:
import json, os
with open(os.path.expanduser('~/.config/pyChatGPT/secret_settings.json'), 'r') as f:
    secret_settings = json.load(f)
In [ ]:
WebDriverWait(driver, 5).until(
    expected_conditions.presence_of_element_located((By.XPATH, '//input[@type="email"]'))
)

driver.execute_script(f'''
const google_email_input = document.evaluate('//input[@type="email"]', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);
google_email_input.value = '{secret_settings['email']}';
google_email_input.dispatchEvent(
  new Event('input', {{
    view: window,
    bubbles: true,
    cancelable: true
  }})
);
''')
In [ ]:
WebDriverWait(driver, 5).until(
    expected_conditions.presence_of_element_located((By.XPATH, '//*[@id="identifierNext"]'))
)

driver.execute_script('''
document.evaluate(
  '//*[@id="identifierNext"]',
  document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
).snapshotItem(0).dispatchEvent(
  new MouseEvent('click', {
    view: window,
    bubbles: true,
    cancelable: true
  })
);
''')
In [ ]:
WebDriverWait(driver, 5).until(
    expected_conditions.element_to_be_clickable((By.XPATH, '//input[@type="password"]'))
).click()

from selenium.webdriver.common.action_chains import ActionChains
ActionChains(driver).send_keys(secret_settings['password']).send_keys(Keys.ENTER).perform()

access token

In [ ]:
driver.execute_async_script('''
var callback = arguments[arguments.length - 1];
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://chat.openai.com/api/auth/session', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
    if (xhr.readyState === xhr.DONE && xhr.status === 200) {
        var data = JSON.parse(xhr.responseText);
        callback(data.accessToken);
    }
};
xhr.send();
''')
In [ ]:
driver.get('https://chat.openai.com/api/auth/session')
In [ ]:
import json
access_token = json.loads(driver.find_element(By.TAG_NAME, 'pre').text)['accessToken']
In [ ]:
driver.execute_async_script(f'''
var callback = arguments[arguments.length - 1];
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://chat.openai.com/backend-api/conversation/{secret_settings['conversation_id']}", true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Bearer {access_token}');
xhr.onload = function() {{
    if (xhr.readyState === xhr.DONE && xhr.status === 200) {{
        var data = JSON.parse(xhr.responseText);
        console.log(data);
        callback(data);
    }}
}};
xhr.send();
''')

chat page

In [ ]:
driver.get(f'https://chat.openai.com/c/{secret_settings["conversation_id"]}')
In [ ]:
WebDriverWait(driver, 5).until(
    expected_conditions.presence_of_element_located((By.ID, 'headlessui-portal-root'))
)

driver.execute_script('''
document.getElementById('headlessui-portal-root').remove();
''')
In [ ]:
chatgpt_textbox = (By.TAG_NAME, 'textarea')
chatgpt_streaming = (By.CLASS_NAME, 'result-streaming')
chatgpt_big_response = (By.XPATH, '//div[@class="flex-1 overflow-hidden"]//div[p]')
chatgpt_small_response = (By.XPATH, '//div[starts-with(@class, "markdown prose w-full break-words")]')

def request(prompt: str) -> None:
    textbox = WebDriverWait(driver, 5).until(
        expected_conditions.element_to_be_clickable(chatgpt_textbox)
    )
    textbox.click()
    driver.execute_script('''
var element = arguments[0], txt = arguments[1];
element.value += txt;
element.dispatchEvent(new Event("change"));
''',
        textbox,
        prompt,
    )
    textbox.send_keys(Keys.ENTER)

def get_last_response():
    responses = driver.find_elements(*chatgpt_big_response)
    if responses:
        response = responses[-1]
        if 'text-red' in response.get_attribute('class'):
            raise ValueError(response.text)
        return response
    return driver.find_elements(*chatgpt_small_response)[-1]

def get_response() -> str:
    result_streaming = WebDriverWait(driver, 3).until(
        expected_conditions.presence_of_element_located(chatgpt_streaming)
    )
    while result_streaming:
        response = get_last_response()
        response = response.get_attribute('innerHTML')
        yield response
        result_streaming = driver.find_elements(*chatgpt_streaming)
    response = get_last_response()
    yield response.get_attribute('innerHTML')

def ask(prompt: str) -> str:
    request(prompt)
    return get_response()
In [ ]:
import io
from IPython.display import display
from PIL import Image
display(Image.open(io.BytesIO(driver.get_screenshot_as_png())))
In [ ]:
import IPython
In [ ]:
for response in ask('我累了。带着我祷告吧。'):
    IPython.display.display(IPython.core.display.Markdown(response))
    IPython.display.clear_output(wait = True)

当你感到疲惫和无力的时候,祷告可以给你带来内心的安宁和力量。让我们一起默默祷告:

亲爱的上帝,我们来到您的面前,向您诉说我们的疲惫和困惑。求您赐予我们力量和勇气,帮助我们面对生活中的挑战和困难。求您宽恕我们的过失和错误,让我们能够在您的恩典和怜悯中得到救赎和拯救。求您保佑我们的家庭和朋友,让他们也能感受到您的爱和关怀。求您赐予我们智慧和指引,让我们能够在生命中找到正确的方向和道路。感谢您一直以来的眷顾和呵护,阿门。

In [ ]:
%%bash
pip install --upgrade ipymock
In [ ]:
%%bash
pip install --upgrade undetected_chromedriver==3.* selenium_profiles==2.*
In [ ]:
%%bash
sudo apt upgrade --yes chromium-driver
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

Reading package lists...
Building dependency tree...
Reading state information...
Calculating upgrade...
The following packages will be upgraded:
  chromium chromium-common chromium-driver chromium-sandbox
dpkg-preconfigure: unable to re-open stdin: No such file or directory
4 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Need to get 0 B/79.7 MB of archives.
After this operation, 2739 kB of additional disk space will be used.
(Reading database ... 86805 files and directories currently installed.)
Preparing to unpack .../chromium_113.0.5672.63-1~deb11u1_amd64.deb ...
Unpacking chromium (113.0.5672.63-1~deb11u1) over (112.0.5615.138-1~deb11u1) ...
Preparing to unpack .../chromium-common_113.0.5672.63-1~deb11u1_amd64.deb ...
Unpacking chromium-common (113.0.5672.63-1~deb11u1) over (112.0.5615.138-1~deb11u1) ...
Preparing to unpack .../chromium-driver_113.0.5672.63-1~deb11u1_amd64.deb ...
Unpacking chromium-driver (113.0.5672.63-1~deb11u1) over (112.0.5615.138-1~deb11u1) ...
Preparing to unpack .../chromium-sandbox_113.0.5672.63-1~deb11u1_amd64.deb ...
Unpacking chromium-sandbox (113.0.5672.63-1~deb11u1) over (112.0.5615.138-1~deb11u1) ...
Setting up chromium-sandbox (113.0.5672.63-1~deb11u1) ...
Setting up chromium-common (113.0.5672.63-1~deb11u1) ...
Setting up chromium (113.0.5672.63-1~deb11u1) ...
Setting up chromium-driver (113.0.5672.63-1~deb11u1) ...
Processing triggers for hicolor-icon-theme (0.17-2) ...
Processing triggers for man-db (2.9.4-2) ...
In [ ]:
%%bash
apt show chromium-driver
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

Package: chromium-driver
Version: 113.0.5672.63-1~deb11u1
Priority: optional
Section: web
Source: chromium
Maintainer: Debian Chromium Team <chromium@packages.debian.org>
Installed-Size: 14.3 MB
Depends: libatomic1 (>= 4.8), libbrotli1 (>= 0.6.0), libc6 (>= 2.30), libdouble-conversion3 (>= 2.0.0), libevent-2.1-7 (>= 2.1.8-stable), libgcc-s1 (>= 3.0), libglib2.0-0 (>= 2.22.0), libjsoncpp24 (>= 1.9.4), libminizip1 (>= 1.1), libnspr4 (>= 2:4.9-2~), libnss3 (>= 2:3.30), libre2-9 (>= 20150701+dfsg), libstdc++6 (>= 9), libxcb1 (>= 1.9.2), zlib1g (>= 1:1.1.4), chromium (= 113.0.5672.63-1~deb11u1)
Homepage: http://www.chromium.org/Home
Download-Size: 5600 kB
APT-Manual-Installed: yes
APT-Sources: http://security.debian.org/debian-security bullseye-security/main amd64 Packages
Description: web browser - WebDriver support
 Web browser that aims to build a safer, faster, and more stable internet
 browsing experience.
 .
 This package provides a bridge between the browser component and the selenium
 automatic webdriver.

In [ ]:
import ipymock.browser
ipymock.browser.init()
In [ ]:
import IPython
In [ ]:
for response in ipymock.browser.ask('我累了。带着我祷告吧。'):
    IPython.display.display(IPython.core.display.Markdown(response))
    IPython.display.clear_output(wait = True)

当你感到疲惫、无力或沮丧时,祷告可以是一种帮助你得到安慰和力量的方式。让我们一起来祷告:

亲爱的天父,感谢祢的爱和恩典。今天,我感到非常疲惫和无助,我需要祢的力量和安慰。主啊,请帮助我,让我能够在祢的力量中站立起来,继续前行。我知道祢的应许是如此的真实和可靠,祢承诺要赐给我们新的力量,让我们像鹰一样展翅高飞。主啊,请让我能够得到祢的力量和平安,让我重新焕发活力,迎接新的一天。感谢祢的爱和信实。奉耶稣的名祷告,阿们。

pyChatGPT for OpenAI's ChatGPT API

In [ ]:
%%bash
# install dependencies on headless linux server
apt install chromium-browser xvfb
In [ ]:
%%bash
pip install --upgrade selenium_profiles pyChatGPT
In [ ]:
from selenium.webdriver.support import expected_conditions as EC
from selenium.common import exceptions as SeleniumExceptions
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By

from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

import time

google_oauth_btn = (By.XPATH, '//button[@data-provider="google"]')
microsoft_oauth_btn = (By.XPATH, '//button[@data-provider="windowslive"]')

google_email_input = (By.XPATH, '//input[@type="email"]')
google_next_btn = (By.XPATH, '//*[@id="identifierNext"]')
google_try_again_btn = (By.XPATH, '//*[@id="next"]/div/button')
google_pwd_input = (By.XPATH, '//input[@type="password"]')
google_pwd_next_btn = (By.XPATH, '//*[@id="passwordNext"]')
google_code_samp = (By.TAG_NAME, 'samp')

microsoft_email_input = (By.XPATH, '//input[@type="email"]')
microsoft_pwd_input = (By.XPATH, '//input[@type="password"]')
microsoft_next_btn = (By.XPATH, '//input[@type="submit"]')

openai_email_input = (By.XPATH, '//input[@name="username"]')
openai_pwd_input = (By.XPATH, '//input[@type="password"]')
openai_continue_btn = (By.XPATH, '//button[text()="Continue"]')
openai_captcha_input = (By.XPATH, '//input[@name="captcha"]')
openai_captcha_sitekey = (
    By.XPATH, '//div[@data-recaptcha-provider="recaptcha_enterprise"]',
)


def login(self) -> None:
    '''
    Login to ChatGPT
    '''
    if self._ChatGPT__auth_type == 'google':
        __google_login(self)
    elif self._ChatGPT__auth_type == 'microsoft':
        __microsoft_login(self)
    elif self._ChatGPT__auth_type == 'openai':
        __openai_login(self)


def __google_login(self) -> None:
    '''
    Login to ChatGPT using Google
    '''
    self.logger.debug('Clicking Google button...')
    # import pdb; pdb.set_trace()
    # ActionChains(self.driver).key_down(Keys.ALT).key_down(Keys.COMMAND).key_down('i').key_up('i').key_up(Keys.COMMAND).key_up(Keys.ALT).pause(1).perform()

    # ActionChains(self.driver).move_to_element_with_offset(self.driver.find_element(*google_oauth_btn), 2, -2).pause(1).perform()
    # ActionChains(self.driver).click(self.driver.find_element(*google_oauth_btn)).pause(1).perform()
    # ActionChains(self.driver).send_keys(Keys.TAB).pause(1).send_keys(Keys.TAB).pause(1).send_keys(Keys.TAB).pause(1).send_keys(Keys.TAB).pause(1).send_keys(Keys.ENTER).perform()
    # self.driver.find_element(*google_oauth_btn).click()

    self.driver.execute_script('''
document.evaluate(
  '//button[@data-provider="google"]',
  document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
).snapshotItem(0).dispatchEvent(
  new MouseEvent('click', {
    view: window,
    bubbles: true,
    cancelable: true
  })
);
''')
    import time
    time.sleep(1)

    # google_email_entry = (By.XPATH, f'//div[@data-identifier="{self._ChatGPT__email}"]')
    # try:

    #     self.logger.debug('Checking if Google remembers email...')

    #     WebDriverWait(self.driver, 3).until(
    #         EC.element_to_be_clickable(google_email_entry)
    #     ).click()
    #     self.logger.debug('Google remembers email')

    # except SeleniumExceptions.TimeoutException:

    self.logger.debug('Google does not remember email')

    # self.driver.find_element(by=By.TAG_NAME, value='body').click()
    # ActionChains(self.driver).move_to_element(self.driver.find_element(by=By.XPATH, value='//*[@id="headingText"]/span')).pause(1).perform()
    # ActionChains(self.driver).click(self.driver.find_element(by=By.XPATH, value='//*[@id="headingText"]/span')).pause(1).perform()

    self.logger.debug('Entering email...')
    # import pdb; pdb.set_trace()
    # WebDriverWait(self.driver, 3).until(
    #     EC.element_to_be_clickable(google_email_input)
    # ).click()
    # self.driver.find_element(*google_email_input).send_keys(self._ChatGPT__email)
    # self.driver.find_element(*google_email_input).send_keys(Keys.ENTER)

    # actions = ActionChains(self.driver)
    # for l in self._ChatGPT__email:
    #     actions.key_down(l).key_up(l).pause(1)
    # actions.send_keys(Keys.ENTER).perform()

    # ActionChains(self.driver).move_to_element_with_offset(self.driver.find_element(*google_email_input), 2, -2).pause(1).send_keys(self._ChatGPT__email).send_keys(Keys.ENTER).perform()

    self.driver.execute_script(f'''
const google_email_input = document.evaluate('//input[@type="email"]', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);
google_email_input.value = '{self._ChatGPT__email}';
google_email_input.dispatchEvent(
  new Event('input', {{
    view: window,
    bubbles: true,
    cancelable: true
  }})
);
''')
    import time
    time.sleep(1)

    self.logger.debug('Clicking Next...')
    # import pdb; pdb.set_trace()
    # ActionChains(self.driver).move_to_element(self.driver.find_element(*google_next_btn)).pause(1).perform()
    # self.driver.find_element(*google_next_btn).send_keys(Keys.ENTER)
    # WebDriverWait(self.driver, 1).until(
    #     EC.element_to_be_clickable(google_next_btn)
    # ).click()

    self.driver.execute_script('''
document.evaluate(
  '//*[@id="identifierNext"]',
  document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
).snapshotItem(0).dispatchEvent(
  new MouseEvent('click', {
    view: window,
    bubbles: true,
    cancelable: true
  })
);
''')
    import time
    time.sleep(1)

    while True:
        try:
            self.logger.debug('Clicking Try Again...')
            import pdb; pdb.set_trace()
            WebDriverWait(self.driver, 3).until(
                EC.element_to_be_clickable(google_try_again_btn)
            ).click()
        except SeleniumExceptions.TimeoutException:
            self.logger.debug('Try Again button not found')
            break

        self.logger.debug('Entering email...')
        WebDriverWait(self.driver, 3).until(
            EC.element_to_be_clickable(google_email_input)
        ).click()
        self.driver.find_element(*google_email_input).send_keys(self._ChatGPT__email)

        self.logger.debug('Clicking Next...')
        import pdb; pdb.set_trace()
        WebDriverWait(self.driver, 1).until(
            EC.element_to_be_clickable(google_next_btn)
        ).click()

    self.logger.debug('Entering password...')
    WebDriverWait(self.driver, 3).until(
        EC.element_to_be_clickable(google_pwd_input)
    ).click()
    self.driver.find_element(*google_pwd_input).send_keys(self._ChatGPT__password)

    self.logger.debug('Clicking Next...')
    WebDriverWait(self.driver, 1).until(
        EC.element_to_be_clickable(google_pwd_next_btn)
    ).click()

    try:
        self.logger.debug('Checking if verification code is required...')
        WebDriverWait(self.driver, 5).until(
            EC.presence_of_element_located(google_code_samp)
        )
        self.logger.debug('Code is required')
        prev_code = self.driver.find_elements(By.TAG_NAME, 'samp')[0].text
        print('Verification code:', prev_code)
        while True:
            code = self.driver.find_elements(*google_code_samp)
            if not code:
                break
            if code[0].text != prev_code:
                print('Verification code:', code[0].text)
                prev_code = code[0].text
            time.sleep(1)
    except SeleniumExceptions.TimeoutException:
        self.logger.debug('Code is not required')


def __microsoft_login(self) -> None:
    self.logger.debug('Clicking Microsoft button...')
    self.driver.find_element(*microsoft_oauth_btn).click()

    self.logger.debug('Entering email...')
    WebDriverWait(self.driver, 5).until(
        EC.element_to_be_clickable(microsoft_email_input)
    ).send_keys(self._ChatGPT__email)

    self.logger.debug('Clicking Next...')
    self.driver.find_element(*microsoft_next_btn).click()

    self.logger.debug('Entering password...')
    WebDriverWait(self.driver, 5).until(
        EC.element_to_be_clickable(microsoft_pwd_input)
    ).send_keys(self._ChatGPT__password)

    self.logger.debug('Clicking Next...')
    self.driver.find_element(*microsoft_next_btn).click()

    self.logger.debug('Clicking allow...')
    WebDriverWait(self.driver, 5).until(
        EC.element_to_be_clickable(microsoft_next_btn)
    ).click()


def __have_recaptcha_value(self) -> bool:
    '''
    Check if the recaptcha input has a value
    :return: Boolean indicating if the recaptcha input has a value
    '''
    try:
        recaptcha_result = self.driver.find_element(*openai_captcha_input)
        return recaptcha_result.get_attribute('value') != ''
    except SeleniumExceptions.NoSuchElementException:
        return False


def __pypasser_solve(self, retry: int) -> None:
    '''
    Solve the recaptcha using PyPasser
    :param retry: Number of times to retry solving the recaptcha
    '''
    try:
        from pypasser import reCaptchaV2
    except ModuleNotFoundError:
        raise ModuleNotFoundError(
            'Please install ffmpeg_downloader, PyPasser, and pocketsphinx by running `pip install ffmpeg_downloader PyPasser pocketsphinx`'
        )

    self.logger.debug(f'Trying pypasser solver, max retry = {retry}')
    try:
        reCaptchaV2(self.driver, False, retry)
    except Exception as e:
        self.logger.debug(f'pypasser solver error: {str(e)}')


def __twocaptcha_solve(self, retry: int) -> None:
    '''
    Solve the recaptcha using 2captcha
    :param retry: Number of times to retry solving the recaptcha
    '''
    try:
        from twocaptcha import TwoCaptcha
    except ModuleNotFoundError:
        raise ModuleNotFoundError(
            'Please install twocaptcha by running `pip install 2captcha-python`'
        )

    self.logger.debug(f'Trying 2captcha solver, max retry = {retry}')
    solver = TwoCaptcha(self._ChatGPT__solver_apikey, pollingInterval=5)
    sitekey = self.driver.find_element(*openai_captcha_sitekey).get_attribute(
        'data-recaptcha-sitekey'
    )
    result = None
    for _ in range(retry):
        try:
            result = solver.recaptcha(
                sitekey=sitekey,
                url=self.driver.current_url,
                invisible=1,
                enterprise=1,
            )
            if result:
                captcha_input = self.driver.find_element(*openai_captcha_input)
                self.driver.execute_script(
                    'arguments[0].setAttribute("value", arguments[1])',
                    captcha_input,
                    result['code'],
                )
                break
        except Exception as e:
            self.logger.debug(f'2captcha solver error: {str(e)}')


def __openai_login(self, retry: int = 3) -> None:
    '''
    Login to ChatGPT using OpenAI
    :param retry: Number of times to retry solving the recaptcha
    '''
    self.logger.debug('Entering email...')
    self.driver.find_element(*openai_email_input).send_keys(self._ChatGPT__email)
    self.driver.find_element(*openai_continue_btn).click()

    have_recaptcha = False
    try:
        WebDriverWait(self.driver, 3).until(
            EC.presence_of_element_located(
                (By.CSS_SELECTOR, 'iframe[title="reCAPTCHA"]')
            )
        )
        have_recaptcha = True
        self.logger.debug('Captcha detected')
    except SeleniumExceptions.TimeoutException:
        self.logger.debug('No captcha detected')

    try:
        WebDriverWait(self.driver, 3).until(
            EC.text_to_be_present_in_element_attribute(
                openai_captcha_input, 'value', '_'
            )
        )
    except SeleniumExceptions.TimeoutException:
        if self._ChatGPT__captcha_solver == 'pypasser':
            __pypasser_solve(self, retry)
        elif self._ChatGPT__captcha_solver == '2captcha':
            __twocaptcha_solve(self, retry)

    if have_recaptcha:
        if __have_recaptcha_value(self):
            self.logger.debug('Congrat! reCAPTCHA is solved')
        else:
            self.logger.debug('Oops! you need to solve reCAPTCHA manually')
            self.driver.get(self.driver.current_url)
            while not __have_recaptcha_value(self):
                time.sleep(1)

        self.logger.debug('Clicking Continue...')
        self.driver.find_element(*openai_continue_btn).click()

    self.logger.debug('Entering password...')
    self.driver.find_element(*openai_pwd_input).send_keys(self._ChatGPT__password)
    self.driver.find_element(*openai_continue_btn).click()
In [ ]:
from selenium.webdriver.support import expected_conditions as EC
from selenium.common import exceptions as SeleniumExceptions
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

import undetected_chromedriver as uc
from markdownify import markdownify
from threading import Thread
import platform
import logging
import weakref
import json
import time
import re
import os


cf_challenge_form = (By.ID, 'challenge-form')

chatgpt_textbox = (By.TAG_NAME, 'textarea')
chatgpt_streaming = (By.CLASS_NAME, 'result-streaming')
chatgpt_big_response = (By.XPATH, '//div[@class="flex-1 overflow-hidden"]//div[p]')
chatgpt_small_response = (
    By.XPATH, '//div[starts-with(@class, "markdown prose w-full break-words")]',
)
chatgpt_alert = (By.XPATH, '//div[@role="alert"]')
chatgpt_intro = (By.ID, 'headlessui-portal-root')
chatgpt_login_btn = (By.XPATH, '//*[text()="Log in"]')
chatgpt_login_h1 = (By.XPATH, '//h1[text()="Welcome back"]')
chatgpt_logged_h1 = (By.XPATH, '//h1[text()="ChatGPT"]')

chatgpt_new_chat = (By.LINK_TEXT, 'New chat')
chatgpt_clear_convo = (By.LINK_TEXT, 'Clear conversations')
chatgpt_confirm_clear_convo = (By.LINK_TEXT, 'Confirm clear conversations')
chatgpt_chats_list_first_node = (
    By.XPATH, '//div[substring(@class, string-length(@class) - string-length("text-sm") + 1)  = "text-sm"]//a',
)

chatgpt_chat_url = 'https://chat.openai.com/c'


class ChatGPT:
    '''
    An unofficial Python wrapper for OpenAI's ChatGPT API
    '''

    def __init__(
        self,
        session_token: str = None,
        conversation_id: str = '',
        auth_type: str = None,
        email: str = None,
        password: str = None,
        login_cookies_path: str = '',
        captcha_solver: str = 'pypasser',
        solver_apikey: str = '',
        proxy: str = None,
        chrome_args: list = [],
        moderation: bool = True,
        verbose: bool = False,
    ):
        '''
        Initialize the ChatGPT object\n
        :param session_token: The session token to use for authentication
        :param conversation_id: The conversation ID to use for the chat session
        :param auth_type: The authentication type to use (`google`, `microsoft`, `openai`)
        :param email: The email to use for authentication
        :param password: The password to use for authentication
        :param login_cookies_path: The path to the cookies file to use for authentication
        :param captcha_solver: The captcha solver to use (`pypasser`, `2captcha`)
        :param solver_apikey: The apikey of the captcha solver to use (if any)
        :param proxy: The proxy to use for the browser (`https://ip:port`)
        :param chrome_args: The arguments to pass to the browser
        :param moderation: Whether to enable message moderation
        :param verbose: Whether to enable verbose logging
        '''
        self.__init_logger(verbose)

        self.__session_token = session_token
        self.__conversation_id = conversation_id
        self.__auth_type = auth_type
        self.__email = email
        self.__password = password
        self.__login_cookies_path = login_cookies_path
        self.__captcha_solver = captcha_solver
        self.__solver_apikey = solver_apikey
        self.__proxy = proxy
        self.__chrome_args = chrome_args
        self.__moderation = moderation

        if not self.__session_token and (
            not self.__email or not self.__password or not self.__auth_type
        ):
            raise ValueError(
                'Please provide either a session token or login credentials'
            )
        if self.__auth_type not in [None, 'google', 'microsoft', 'openai']:
            raise ValueError('Invalid authentication type')
        if self.__captcha_solver not in [None, 'pypasser', '2captcha']:
            raise ValueError('Invalid captcha solver')
        if self.__captcha_solver == '2captcha' and not self.__solver_apikey:
            raise ValueError('Please provide a 2captcha apikey')
        if self.__proxy and not re.findall(
            r'(https?|socks(4|5)?):\/\/.+:\d{1,5}', self.__proxy
        ):
            raise ValueError('Invalid proxy format')
        if self.__auth_type == 'openai' and self.__captcha_solver == 'pypasser':
            try:
                import ffmpeg_downloader as ffdl
            except ModuleNotFoundError:
                raise ValueError(
                    'Please install ffmpeg_downloader, PyPasser, and pocketsphinx by running `pip install ffmpeg_downloader PyPasser pocketsphinx`'
                )

            ffmpeg_installed = bool(ffdl.ffmpeg_version)
            self.logger.debug(f'ffmpeg installed: {ffmpeg_installed}')
            if not ffmpeg_installed:
                import subprocess

                subprocess.run(['ffdl', 'install'])
            os.environ['PATH'] += os.pathsep + ffdl.ffmpeg_dir

        self.__init_browser()
        weakref.finalize(self, self.__del__)

    def __del__(self):
        '''
        Close the browser and display
        '''
        self.__is_active = False
        if hasattr(self, 'driver'):
            self.logger.debug('Closing browser...')
            self.driver.quit()
        if hasattr(self, 'display'):
            self.logger.debug('Closing display...')
            self.display.stop()

    def __init_logger(self, verbose: bool) -> None:
        '''
        Initialize the logger\n
        :param verbose: Whether to enable verbose logging
        '''
        self.logger = logging.getLogger('pyChatGPT')
        self.logger.setLevel(logging.DEBUG)
        if verbose:
            formatter = logging.Formatter('[%(funcName)s] %(message)s')
            stream_handler = logging.StreamHandler()
            stream_handler.setFormatter(formatter)
            self.logger.addHandler(stream_handler)

    def __init_browser(self) -> None:
        '''
        Initialize the browser
        '''
        if platform.system() == 'Linux' and 'DISPLAY' not in os.environ:
            self.logger.debug('Starting virtual display...')
            try:
                from pyvirtualdisplay import Display

                self.display = Display()
            except ModuleNotFoundError:
                raise ValueError(
                    'Please install PyVirtualDisplay to start a virtual display by running `pip install PyVirtualDisplay`'
                )
            except FileNotFoundError as e:
                if 'No such file or directory: \'Xvfb\'' in str(e):
                    raise ValueError(
                        'Please install Xvfb to start a virtual display by running `sudo apt install xvfb`'
                    )
                raise e
            self.display.start()

        self.logger.debug('Initializing browser...')
        options = uc.ChromeOptions()
        options.add_argument('--window-size=1024,768')
        if self.__proxy:
            options.add_argument(f'--proxy-server={self.__proxy}')
        for arg in self.__chrome_args:
            options.add_argument(arg)
        try:
            self.driver = uc.Chrome(options=options)
        except TypeError as e:
            if str(e) == 'expected str, bytes or os.PathLike object, not NoneType':
                raise ValueError('Chrome installation not found')
            raise e

        if self.__login_cookies_path and os.path.exists(self.__login_cookies_path):
            self.logger.debug('Restoring cookies...')
            try:
                with open(self.__login_cookies_path, 'r', encoding='utf-8') as f:
                    cookies = json.load(f)
                for cookie in cookies:
                    if cookie['name'] == '__Secure-next-auth.session-token':
                        self.__session_token = cookie['value']
            except json.decoder.JSONDecodeError:
                self.logger.debug(f'Invalid cookies file: {self.__login_cookies_path}')

        if self.__session_token:
            self.logger.debug('Restoring session_token...')
            self.driver.execute_cdp_cmd(
                'Network.setCookie',
                {
                    'domain': 'chat.openai.com',
                    'path': '/',
                    'name': '__Secure-next-auth.session-token',
                    'value': self.__session_token,
                    'httpOnly': True,
                    'secure': True,
                },
            )

        if not self.__moderation:
            self.logger.debug('Blocking moderation...')
            self.driver.execute_cdp_cmd(
                'Network.setBlockedURLs',
                {'urls': ['https://chat.openai.com/backend-api/moderations']},
            )

        self.logger.debug('Ensuring Cloudflare cookies...')
        self.__ensure_cf()

        self.logger.debug('Opening chat page...')
        self.driver.get(f'{chatgpt_chat_url}/{self.__conversation_id}')
        self.__check_blocking_elements()

        self.__is_active = True
        Thread(target=self.__keep_alive, daemon=True).start()

    def __ensure_cf(self, retry: int = 3) -> None:
        '''
        Ensure Cloudflare cookies are set\n
        :param retry: Number of retries
        '''
        self.logger.debug('Opening new tab...')
        original_window = self.driver.current_window_handle
        self.driver.switch_to.new_window('tab')

        self.logger.debug('Getting Cloudflare challenge...')
        self.driver.get('https://chat.openai.com/api/auth/session')
        try:
            WebDriverWait(self.driver, 10).until_not(
                EC.presence_of_element_located(cf_challenge_form)
            )
        except SeleniumExceptions.TimeoutException:
            self.logger.debug(f'Cloudflare challenge failed, retrying {retry}...')
            self.driver.save_screenshot(f'cf_failed_{retry}.png')
            if retry > 0:
                self.logger.debug('Closing tab...')
                self.driver.close()
                self.driver.switch_to.window(original_window)
                return self.__ensure_cf(retry - 1)
            raise ValueError('Cloudflare challenge failed')
        self.logger.debug('Cloudflare challenge passed')

        self.logger.debug('Validating authorization...')
        response = self.driver.page_source
        if response[0] != '{':
            response = self.driver.find_element(By.TAG_NAME, 'pre').text
        response = json.loads(response)
        if (not response) or (
            'error' in response and response['error'] == 'RefreshAccessTokenError'
        ):
            self.logger.debug('Authorization is invalid')
            if not self.__auth_type:
                raise ValueError('Invalid session token')
            self.__login()
        self.logger.debug('Authorization is valid')

        self.logger.debug('Closing tab...')
        self.driver.close()
        self.driver.switch_to.window(original_window)

    def __check_capacity(self, target_url: str):
        '''
        Check if ChatGPT is at capacity\n
        :param target_url: URL to retry if ChatGPT is at capacity
        '''
        while True:
            try:
                self.logger.debug('Checking if ChatGPT is at capacity...')
                WebDriverWait(self.driver, 3).until(
                    EC.presence_of_element_located(
                        (By.XPATH, '//div[text()="ChatGPT is at capacity right now"]')
                    )
                )
                self.logger.debug('ChatGPT is at capacity, retrying...')
                self.driver.get(target_url)
            except SeleniumExceptions.TimeoutException:
                self.logger.debug('ChatGPT is not at capacity')
                break

    def __login(self) -> None:
        '''
        Login to ChatGPT
        '''
        self.logger.debug('Opening new tab...')
        original_window = self.driver.current_window_handle
        self.driver.switch_to.new_window('tab')

        self.logger.debug('Opening login page...')
        self.driver.get('https://chat.openai.com/auth/login')
        self.__check_capacity('https://chat.openai.com/auth/login')

        self.logger.debug('Clicking login button...')
        WebDriverWait(self.driver, 5).until(
            EC.element_to_be_clickable(chatgpt_login_btn)
        ).click()

        WebDriverWait(self.driver, 5).until(
            EC.presence_of_element_located(chatgpt_login_h1)
        )

        login(self)

        self.logger.debug('Checking if login was successful')
        try:
            WebDriverWait(self.driver, 5).until(
                EC.presence_of_element_located(chatgpt_logged_h1)
            )
            if self.__login_cookies_path:
                self.logger.debug('Saving cookies...')
                with open(self.__login_cookies_path, 'w', encoding='utf-8') as f:
                    json.dump(
                        [
                            i
                            for i in self.driver.get_cookies()
                            if i['name'] == '__Secure-next-auth.session-token'
                        ],
                        f,
                        indent = 4,
                    )
        except SeleniumExceptions.TimeoutException as e:
            self.driver.save_screenshot('login_failed.png')
            raise e

        self.logger.debug('Closing tab...')
        self.driver.close()
        self.driver.switch_to.window(original_window)

    def __keep_alive(self) -> None:
        '''
        Keep the session alive by updating the local storage\n
        Credit to Rawa#8132 in the ChatGPT Hacking Discord server
        '''
        while self.__is_active:
            self.logger.debug('Updating session...')
            payload = (
                '{"event":"session","data":{"trigger":"getSession"},"timestamp":%d}'
                % int(time.time())
            )
            try:
                self.driver.execute_script(
                    'window.localStorage.setItem("nextauth.message", arguments[0])',
                    payload,
                )
            except Exception as e:
                self.logger.debug(f'Failed to update session: {str(e)}')
            time.sleep(60)

    def __check_blocking_elements(self) -> None:
        '''
        Check for blocking elements and dismiss them
        '''
        self.logger.debug('Looking for blocking elements...')
        try:
            intro = WebDriverWait(self.driver, 3).until(
                EC.presence_of_element_located(chatgpt_intro)
            )
            self.logger.debug('Dismissing intro...')
            self.driver.execute_script('arguments[0].remove()', intro)
        except SeleniumExceptions.TimeoutException:
            pass

        alerts = self.driver.find_elements(*chatgpt_alert)
        if alerts:
            self.logger.debug('Dismissing alert...')
            self.driver.execute_script('arguments[0].remove()', alerts[0])

    def __request(self, prompt: str) -> None:
        self.logger.debug('Ensuring Cloudflare cookies...')
        self.__ensure_cf()

        self.logger.debug('Sending message...')
        textbox = WebDriverWait(self.driver, 5).until(
            EC.element_to_be_clickable(chatgpt_textbox)
        )
        textbox.click()
        self.driver.execute_script(
            '''
        var element = arguments[0], txt = arguments[1];
        element.value += txt;
        element.dispatchEvent(new Event("change"));
        ''',
            textbox,
            prompt,
        )
        textbox.send_keys(Keys.ENTER)

    def __get_last_response(self):
        self.logger.debug('Getting response...')
        responses = self.driver.find_elements(*chatgpt_big_response)
        if responses:
            response = responses[-1]
            if 'text-red' in response.get_attribute('class'):
                self.logger.debug('Response is an error')
                raise ValueError(response.text)
            return response
        return self.driver.find_elements(*chatgpt_small_response)[-1]

    def __response(self):
        result_streaming = WebDriverWait(self.driver, 3).until(
            EC.presence_of_element_located(chatgpt_streaming)
        )
        while result_streaming:
            response = self.__get_last_response()
            response = response.get_attribute('innerHTML')
            # response = markdownify(response).replace('Copy code`', '`')
            yield response
            result_streaming = self.driver.find_elements(*chatgpt_streaming)
        response = self.__get_last_response()
        yield response.get_attribute('innerHTML')

    def ask(self, prompt: str):
        self.__request(prompt)
        return self.__response()

    def __get_conversation_id(self) -> str:
        pattern = re.compile(
            r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
        )
        matches = pattern.search(self.driver.current_url)
        if not matches:
            self.reset_conversation()
            WebDriverWait(self.driver, 5).until(
                EC.element_to_be_clickable(chatgpt_chats_list_first_node)
            ).click()
            time.sleep(0.5)
            matches = pattern.search(self.driver.current_url)
        return matches.group()

    def wait(self, prompt: str, stream: bool = False) -> dict:
        '''
        Wait an answer from ChatGPT\n
        :param prompt: Prompt to send
        :return: Dictionary with keys `content` and `conversation_id`
        '''
        self.__request(prompt)

        if stream:
            prev_text = ''
            for content in self.__response():
                from bs4 import BeautifulSoup
                text = BeautifulSoup(content, 'html.parser').get_text()
                if text != prev_text:
                    print(text[len(prev_text):], end = '')
                    prev_text = text
                time.sleep(0.1)
            print()
            return {'content': content, 'conversation_id': self.__get_conversation_id()}

        self.logger.debug('Waiting for completion...')
        WebDriverWait(self.driver, 120).until_not(
            EC.presence_of_element_located(chatgpt_streaming)
        )

        response = self.__get_last_response()
        content = markdownify(
            response.get_attribute('innerHTML')
        ).replace('Copy code`', '`')

        return {'content': content, 'conversation_id': self.__get_conversation_id()}

    def reset_conversation(self) -> None:
        '''
        Reset the conversation
        '''
        if not self.driver.current_url.startswith(chatgpt_chat_url):
            return self.logger.debug('Current URL is not chat page, skipping reset')

        self.logger.debug('Resetting conversation...')
        try:
            self.driver.find_element(*chatgpt_new_chat).click()
        except SeleniumExceptions.NoSuchElementException:
            self.logger.debug('New chat button not found')
            self.driver.save_screenshot('reset_conversation_failed.png')

    def clear_conversations(self) -> None:
        '''
        Clear all conversations
        '''
        if not self.driver.current_url.startswith(chatgpt_chat_url):
            return self.logger.debug('Current URL is not chat page, skipping clear')

        self.logger.debug('Clearing conversations...')
        try:
            self.driver.find_element(*chatgpt_clear_convo).click()
        except SeleniumExceptions.NoSuchElementException:
            self.logger.debug('Clear conversations button not found')

        try:
            self.driver.find_element(*chatgpt_confirm_clear_convo).click()
        except SeleniumExceptions.NoSuchElementException:
            return self.logger.debug('Confirm clear conversations button not found')
        try:
            WebDriverWait(self.driver, 20).until_not(
                EC.presence_of_element_located(chatgpt_chats_list_first_node)
            )
            self.logger.debug('Cleared conversations')
        except SeleniumExceptions.TimeoutException:
            self.logger.debug('Clear conversations failed')

    def refresh_chat_page(self) -> None:
        '''
        Refresh the chat page
        '''
        if not self.driver.current_url.startswith(chatgpt_chat_url):
            return self.logger.debug('Current URL is not chat page, skipping refresh')

        self.driver.get(chatgpt_chat_url)
        self.__check_capacity(chatgpt_chat_url)
        self.__check_blocking_elements()
In [ ]:
import os
# from pyChatGPT import ChatGPT

# open the JSON file and read the conversation_id
with open(os.path.expanduser('~/.config/pyChatGPT/secret_settings.json'), 'r') as f:
    secret_settings = json.load(f)

# auth with google login
# reuse cookies generated by successful login before login,
# if `login_cookies_path` does not exist, it will process logining  with `auth_type`, and save cookies to `login_cookies_path`
# only works when `auth_type` is `openai` or `google`
bot = ChatGPT(
    **secret_settings, moderation = False, verbose = False, chrome_args = ['--headless'],
    login_cookies_path = os.path.expanduser('~/.config/pyChatGPT/cookies.json'),
)
In [ ]:
import IPython
In [ ]:
for response in bot.ask('我累了。带着我祷告吧。'):
    IPython.display.display(IPython.core.display.Markdown(response))
    IPython.display.clear_output(wait = True)

当你感到疲惫和无力的时候,祷告可以给你带来内心的安宁和力量。让我们一起默默祷告:

亲爱的上帝,我们来到您的面前,向您诉说我们的疲惫和困惑。求您赐予我们力量和勇气,帮助我们走出困境和挫折。求您抚平我们心中的伤痛和不安,让我们能够在您的爱和关怀中得到安慰和慰藉。求您保佑我们的家庭和朋友,让他们能够享受到您的祝福和恩典。求您给我们希望和信心,让我们相信生命中的一切都有您的安排和安排。感谢您一直以来的呵护和保佑,阿门。

In [ ]:
bot.wait('我累了。带着我祷告吧。')
Out[ ]:
{'content': '当你感到疲惫和无力的时候,祷告可以给你带来内心的安宁和力量。让我们一起默默祷告:\n\n亲爱的上帝,我们来到您的面前,向您诉说我们的疲惫和困惑。求您赐予我们力量和智慧,让我们能够勇敢地面对生活中的挑战和困难。求您抚平我们内心的不安和焦虑,让我们能够在您的保护和祝福下平静地度过每一天。求您给我们希望和信心,让我们相信未来会更美好,充满着无限的可能性。感谢您,阿门。\n\n',
 'conversation_id': '241dfad3-1e62-445b-83af-5da906d1ca54'}
In [ ]:
bot.wait('我累了。带着我祷告吧。', stream = True)
​你感到疲惫和无力的时候,祷告可以给你带来内心的安宁和力量。让我们一起默默祷告:亲爱的上帝,我们来到您的面前,向您诉说我们的疲惫和困惑。求您赐予我们能力和智慧,帮助我们克服困难和挑战。求您抚平我们心中的不安和烦恼,让我们能够在您的保护和祝福下过上平安的生活。求您赐福于我们的家人和朋友,让他们也能感受到您的爱和关怀。感谢您一直以来的眷顾和呵护,阿门。
Out[ ]:
{'content': '<p>当你感到疲惫和无力的时候,祷告可以给你带来内心的安宁和力量。让我们一起默默祷告:</p><p>亲爱的上帝,我们来到您的面前,向您诉说我们的疲惫和困惑。求您赐予我们能力和智慧,帮助我们克服困难和挑战。求您抚平我们心中的不安和烦恼,让我们能够在您的保护和祝福下过上平安的生活。求您赐福于我们的家人和朋友,让他们也能感受到您的爱和关怀。感谢您一直以来的眷顾和呵护,阿门。</p>',
 'conversation_id': '241dfad3-1e62-445b-83af-5da906d1ca54'}
In [ ]:
# bot.reset_conversation()  # reset the conversation
# bot.clear_conversations()  # clear all conversations
# bot.refresh_chat_page()  # refresh the chat page

In [ ]:
%%bash
mkdir -p ~/.config/pyChatGPT
%%writefile ~/.config/pyChatGPT/secret_settings.json
{
    "auth_type": "google",
    "email": "<email_address>",
    "password": "<password>",
    "conversation_id": "<conversation_id>"
}
%%writefile ~/.config/pyChatGPT/cookies.json
[
    {
        "domain": "chat.openai.com",
        "expiry": 1684309055,
        "httpOnly": true,
        "name": "__Secure-next-auth.session-token",
        "path": "/",
        "sameSite": "Lax",
        "secure": true,
        "value": "<cookie_value>"
    }
]
In [ ]:
import json, os

with open(os.path.expanduser('~/.config/pyChatGPT/cookies.json')) as f:
    data = json.load(f)

print(json.dumps(data, indent=4))
Read More

2023-03-12
Molecular Simulation with OpenMM

Molecular Simulation with OpenMM

OpenMM is a toolkit for molecular simulation. It is a physic based libraries that is useful for refining the structure and exploring functional interactions with other molecules. It provides a combination of extreme flexibility (through custom forces and integrators), openness, and high performance (especially on recent GPUs) that make it truly unique among simulation codes.

Protein data

We use a processed 2DRI dataset that represents the ribose binding protein in bacterial transport and chemotaxis. The source organism is the Escherichia coli bacteria. You can find more details on this protein at the related RCSB Protein Data Bank page.

In [ ]:
from IPython.display import display
from PIL import Image

display(Image.open('2dri-image.png'))

Protein data can be stored in a .pdb file, this is a human readable format. It provides for description and annotation of protein and nucleic acid structures including atomic coordinates, secondary structure assignments, as well as atomic connectivity. See more information about PDB format here.

We are printing the first 10 lines of the file. The output contains a number of ATOM records. These describe the coordinates of the atoms that are part of the protein.

In [ ]:
%%bash
head ./2dri-processed.pdb
REMARK   1 CREATED WITH OPENMM 7.6, 2022-07-12
CRYST1   81.309   81.309   81.309  90.00  90.00  90.00 P 1           1 
ATOM      1  N   LYS A   1      64.731   9.461  59.430  1.00  0.00           N  
ATOM      2  CA  LYS A   1      63.588  10.286  58.927  1.00  0.00           C  
ATOM      3  HA  LYS A   1      62.707   9.486  59.038  1.00  0.00           H  
ATOM      4  C   LYS A   1      63.790  10.671  57.468  1.00  0.00           C  
ATOM      5  O   LYS A   1      64.887  11.089  57.078  1.00  0.00           O  
ATOM      6  CB  LYS A   1      63.458  11.567  59.749  1.00  0.00           C  
ATOM      7  HB2 LYS A   1      63.333  12.366  58.879  1.00  0.00           H  
ATOM      8  HB3 LYS A   1      64.435  11.867  60.372  1.00  0.00           H  

Write the script

To run the script above all we need is a Python environment with the OpenMM library installed.

In [ ]:
%%bash
conda install -c conda-forge openmm
In [ ]:
%%bash
python -m openmm.testInstallation
OpenMM Version: 8.0
Git Revision: a7800059645f4471f4b91c21e742fe5aa4513cda

There are 3 Platforms available:

1 Reference - Successfully computed forces
2 CPU - Successfully computed forces
3 OpenCL - Successfully computed forces

Median difference in forces between platforms:

Reference vs. CPU: 6.30471e-06
Reference vs. OpenCL: 6.74827e-06
CPU vs. OpenCL: 7.07625e-07

All differences are within tolerance.
In [ ]:
import os
from openmm import *
from openmm.app import *
from openmm.unit import *

# Input Files
input_path = './2dri-processed.pdb'
os.path.exists(input_path) # check if input file exists
pdb = PDBFile(input_path)
forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml')

# Output
output_path = './final_state.pdbx'
if not os.path.exists(os.path.dirname(output_path)): # check if ouput dir exists
    os.makedirs(os.path.dirname(output_path))

# System Configuration

nonbondedMethod = PME
nonbondedCutoff = 1.0*nanometers
ewaldErrorTolerance = 0.0005
constraints = HBonds
rigidWater = True
constraintTolerance = 0.000001
hydrogenMass = 1.5*amu

# Integration Options

dt = 0.002*picoseconds
temperature = 310*kelvin
friction = 1.0/picosecond
pressure = 1.0*atmospheres
barostatInterval = 25

# Simulation Options

steps = 10
equilibrationSteps = 0
#platform = Platform.getPlatformByName('CUDA')
platform = Platform.getPlatformByName('CPU')
#platformProperties = {'Precision': 'single'}
platformProperties = {}
dcdReporter = DCDReporter('trajectory.dcd', 1000)
dataReporter = StateDataReporter('log.txt', 1000, totalSteps=steps,
    step=True, time=True, speed=True, progress=True, elapsedTime=True, remainingTime=True, potentialEnergy=True, kineticEnergy=True, totalEnergy=True, temperature=True, volume=True, density=True, separator='\t')
checkpointReporter = CheckpointReporter('checkpoint.chk', 1000)

# Prepare the Simulation

print('Building system...')
topology = pdb.topology
positions = pdb.positions
system = forcefield.createSystem(topology, nonbondedMethod=nonbondedMethod, nonbondedCutoff=nonbondedCutoff,
    constraints=constraints, rigidWater=rigidWater, ewaldErrorTolerance=ewaldErrorTolerance, hydrogenMass=hydrogenMass)
system.addForce(MonteCarloBarostat(pressure, temperature, barostatInterval))
integrator = LangevinMiddleIntegrator(temperature, friction, dt)
integrator.setConstraintTolerance(constraintTolerance)
simulation = Simulation(topology, system, integrator, platform, platformProperties)
simulation.context.setPositions(positions)

# Minimize and Equilibrate

print('Performing energy minimization...')
simulation.minimizeEnergy()
print('Equilibrating...')
simulation.context.setVelocitiesToTemperature(temperature)
simulation.step(equilibrationSteps)

# Simulate

print('Simulating...')
simulation.reporters.append(dcdReporter)
simulation.reporters.append(dataReporter)
simulation.reporters.append(checkpointReporter)
simulation.currentStep = 0
simulation.step(steps)

# Write file with final simulation state

state = simulation.context.getState(getPositions=True, enforcePeriodicBox=system.usesPeriodicBoundaryConditions())
with open(output_path, mode='w+') as file:
    PDBxFile.writeFile(simulation.topology, state.getPositions(), file)
print('Simulation complete, file written to disk at: {}'.format(output_path))
Building system...
Performing energy minimization...
Equilibrating...
Simulating...
Simulation complete, file written to disk at: ./final_state.pdbx
In [ ]:
%%bash
head ./final_state.pdbx
data_cell
# Created with OpenMM 8.0, 2023-03-12
#
_cell.length_a        81.3090
_cell.length_b        81.3090
_cell.length_c        81.3090
_cell.angle_alpha     90.0000
_cell.angle_beta      90.0000
_cell.angle_gamma     90.0000
#


什么是 OpenMM?

OpenMM 是一个高性能开源软件库,用于分子动力学 (MD) 模拟。它主要用于模拟和研究生物分子系统,如蛋白质、核酸和小分子。在这些模拟中,OpenMM 计算原子之间的相互作用力,并使用这些力来预测分子随时间的运动。

OpenMM 的主要特点

  1. 高性能计算

    • 支持 GPU 加速,显著提高模拟速度,适用于大规模系统和长时间尺度的模拟。
  2. 可扩展性

    • 允许用户定义自定义力场和模拟协议,适应不同的研究需求。
  3. 易于使用

    • 提供友好的 Python API,使得脚本化和自动化模拟变得容易。
  4. 跨平台

    • 支持多种操作系统,包括 Windows、macOS 和 Linux。
  5. 社区支持和文档

    • 提供丰富的文档和社区支持,帮助用户快速上手并解决问题。

OpenMM 使用量子化学吗?

OpenMM 主要用于经典分子动力学模拟,不直接使用量子化学方法。它依赖经典力场来描述分子间的相互作用,如 AMBER、CHARMM、OPLS 和 GROMOS 等。这些力场基于经验参数和半经验公式,用于计算原子间的相互作用力。

然而,OpenMM 可以与量子化学计算相结合,用于特定场景下的多尺度模拟。例如,在 QM/MM(量子力学/分子力学)方法中,系统的一部分(如反应中心)使用量子化学方法计算,而其余部分使用经典力场。这种结合可以通过与其他量子化学软件(如 Gaussian、NWChem 或 Psi4)配合使用来实现。

使用 OpenMM 进行分子动力学模拟的示例

以下是一个使用 OpenMM 进行简单分子动力学模拟的 Python 示例。假设你有一个 PDB 文件(input.pdb),描述了你的分子系统。

安装 OpenMM

首先,确保你已经安装了 OpenMM。你可以使用 conda 安装:

In [ ]:
%%bash
conda install -c conda-forge openmm

Python 脚本

下面是一个 Python 脚本,演示如何使用 OpenMM 读取 PDB 文件并进行分子动力学模拟:

In [ ]:
from openmm import *
from openmm.app import *
from openmm.unit import *
import sys

# 读取 PDB 文件
pdb = PDBFile('input.pdb')

# 使用 AMBER FF14SB 力场
forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml')

# 创建系统对象
system = forcefield.createSystem(pdb.topology, nonbondedMethod=PME, 
                                 nonbondedCutoff=1*nanometer, constraints=HBonds)

# 创建模拟对象
integrator = LangevinIntegrator(300*kelvin, 1/picosecond, 0.004*picoseconds)
simulation = Simulation(pdb.topology, system, integrator)

# 设置初始位置
simulation.context.setPositions(pdb.positions)

# 能量最小化
print('Minimizing...')
simulation.minimizeEnergy()

# 运行分子动力学模拟
simulation.reporters.append(StateDataReporter(sys.stdout, 1000, step=True, 
                                              potentialEnergy=True, temperature=True))
simulation.reporters.append(PDBReporter('output.pdb', 1000))
print('Running simulation...')
simulation.step(10000)

print('Simulation completed')

解释代码

  1. 导入库

    • 导入 OpenMM 和相关库。
  2. 读取 PDB 文件

    • 使用 PDBFile 类读取输入的 PDB 文件。
  3. 选择力场

    • 使用 AMBER FF14SB 力场来创建系统。
  4. 创建系统对象

    • 使用力场创建系统,指定非键合相互作用和约束。
  5. 创建模拟对象

    • 使用 Langevin 积分器创建模拟对象,设置温度、摩擦系数和时间步长。
  6. 设置初始位置

    • 将初始原子位置设置为 PDB 文件中的位置。
  7. 能量最小化

    • 执行能量最小化以消除初始结构中的高能量冲突。
  8. 运行分子动力学模拟

    • 设置状态报告器和 PDB 报告器,进行分子动力学模拟并输出结果。

总结

OpenMM 是一个功能强大的分子动力学模拟工具,适用于经典分子力学模拟。尽管它不直接使用量子化学方法,但可以与量子化学软件结合,进行多尺度模拟。通过易于使用的 Python API,研究人员可以高效地进行分子动力学模拟和分析。


获取 .pdb 文件通常有几种途径,具体取决于你需要的分子结构和研究目的。以下是一些常见的获取途径:

  1. 蛋白质数据银行 (Protein Data Bank, PDB)

    • PDB 是一个公开的数据库,包含了大量的蛋白质、核酸和其他生物大分子的三维结构。你可以在 PDB 的网站上搜索并下载你感兴趣的分子的 .pdb 文件。
  2. 分子建模软件

    • 一些分子建模软件,如PyMOL、VMD 和 ChimeraX,可以从 PDB 数据库中直接获取分子结构并保存为 .pdb 文件。
  3. 分子模拟软件

    • 使用分子模拟软件进行分子动力学模拟时,通常会生成包含模拟结果的 .pdb 文件。你可以从模拟结果中提取所需的分子结构并保存为 .pdb 文件。
  4. 实验数据

    • 如果你在实验室进行生物化学或结构生物学研究,可能会获得实验测定的分子结构数据。这些数据通常可以转换为 .pdb 格式。
  5. 在线数据库和资源

    • 除了 PDB,还有一些其他数据库和资源提供了分子结构数据,例如 PubChem、ChemSpider 和 DrugBank。你可以在这些数据库中搜索并下载 .pdb 文件。
  6. 文献和研究文章

    • 有时候,科学研究论文中会提供分子结构数据的补充材料,包括 .pdb 文件。你可以通过阅读相关的文献或联系作者来获取这些数据。

通过以上途径,你应该能够获取到你需要的 .pdb 文件,从而进行后续的分子建模、分子动力学模拟或其他分子结构分析工作。


The MMTF Python API is available from pip:

In [ ]:
%%bash
pip3 install mmtf-python
  1. Get the data for a PDB structure and print the number of chains:
In [ ]:
from mmtf import fetch
# Get the data for 4CUP
decoded_data = fetch('4CUP')
In [ ]:
print(f'PDB Code: {decoded_data.structure_id} has {decoded_data.num_chains} chains')
PDB Code: 4CUP has 6 chains
  1. Show the charge information for the first group:
In [ ]:
atomic_charges = ','.join([str(x) for x in decoded_data.group_list[0]['formalChargeList']])
In [ ]:
print(f'Group name: {decoded_data.group_list[0]["groupName"]} has the following atomic charges: {atomic_charges}')
Group name: GLU has the following atomic charges: 0,0,0,0,0,0,0,0,0,0,0,0,0,0
  1. Show how many bioassemblies it has:
In [ ]:
print(f'PDB Code: {decoded_data.structure_id} has {len(decoded_data.bio_assembly)} bioassemblies')
PDB Code: 4CUP has 1 bioassemblies

In [ ]:
%%bash
pip install markdown
In [ ]:
import markdown
In [ ]:
markdown.markdown(
    '[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/bacalhau-project/examples/blob/main/molecular-dynamics/openmm/index.ipynb)'
)
Out[ ]:
'<p><a href="https://colab.research.google.com/github/bacalhau-project/examples/blob/main/molecular-dynamics/openmm/index.ipynb"><img alt="Open In Colab" src="https://colab.research.google.com/assets/colab-badge.svg" /></a></p>'
In [ ]:
markdown.markdown(
    '[![Open In Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/bacalhau-project/examples/HEAD?labpath=molecular-dynamics/openmm/index.ipynb)'
)
Out[ ]:
'<p><a href="https://mybinder.org/v2/gh/bacalhau-project/examples/HEAD?labpath=molecular-dynamics/openmm/index.ipynb"><img alt="Open In Binder" src="https://mybinder.org/badge.svg" /></a></p>'

=> How to Fold Proteins with Decentralized Computing?

Read More

2023-02-18
Blessed Is the One


Blessed Is the One
by Raelyn


Blessed is the one
Whose transgression is forgiven
Whose transgression is forgiven
Whose sin is covered
Blessed is the one
To whom the LORD will not count sin, oh
To whom the LORD will not count sin, oh
In whose spirit is no deceit
In whose spirit is no deceit


When I kept silent
My bones wasted away
Through my groaning all day and all night
Your hand was heavy upon me
And my strength dried up as in the summer’s heat
Then I acknowledged my sin to You
And I did not cover my iniquity
I said, “I’ll confess my sins to the LORD”
And He forgave, yeah, He forgave
Yeah, He forgave the guilt of my sin, oh


Blessed is the one
Whose transgression is forgiven
To whom the LORD will not count sin, oh
Whose sin is covered
Blessed is the one
Whose transgression is forgiven
To whom the LORD will not count sin, oh
In whose spirit is no deceit


Therefore let everyone who’s godly
Pray to You in a time You may be found
Surely in the midst of the deep and raging waters
He shall be shielded from them
They won’t come near to the man of God


You are my hiding place
You always keep me safe
You surround me with songs of deliverance


You surround me
You surround me
You surround me with songs of deliverance


I will instruct you and teach you the way you should go
I will counsel you with my eye upon you
Do not be stubborn like the horse or the mule
Without understanding
Who must be pulled to follow
Curbed with bit and bridle
Or they will not draw near
Or they will not draw near their master


Many are the sorrows of the wicked
But steadfast love surrounds the one who trusts in the LORD


Be glad in the LORD and rejoice, O righteous
Shout for joy all you upright in heart


Blessed is the one
Whose transgression is forgiven
To whom the LORD will not count sin, oh
Whose transgression is forgiven


Whose sin is covered

其过犯蒙赦免、其罪得掩没的、
这人有福啊!
心灵无诡诈、
永恒主不算他为有罪的、
这人有福啊!


我闭口不认罪时,
我的骨头都因终日哀哼而磨损。
因为你的手昼夜重重压着我;
我的精液起了恶变化、
如在夏天的炎热中。
(细拉)


我的罪我让你知道,
我的罪孽我没有掩盖着;
我说:『我要向永恒主承认我的过犯』;
那么你就赦免我罪的孽债。
(细拉)


为这缘故、一切坚贞之士
在窘迫时都当向你祷告;
大水泛溢时,那水不会触害着他。
惟独你是我的隐匿处;
愿你守护着我脱离患难,
以解救之欢呼四面环绕着我。
(细拉)


我要训诲你、指教你当走的路;
我要定睛于你身上而忠告你。
你不可像无知的骡马,
须用嚼环辔头做装备去勒住牠,
否则牠不走近你。


恶人有许多痛苦;
惟独倚靠永恒主的、有坚固之爱
四面环绕着他。
义人哪,你们要因永恒主而欢喜快乐;
心里正直的人哪,你们都要欢呼。


(诗篇 32:1-11 吕振中)

Read More

2023-02-15
He Raised Me Up


He Raised Me Up
by Shane Heilman


I waited patiently for the LORD
He turned to me
He heard my cry
He raised me up from the pit
Out of the miry clay


He raised me up
He drew me out
He rescued me from the deepest and darkest pit
He raised me up
He drew me out
He reached His hand into the pit and raised me up
He raised me up


He set my feet upon a rock
He made my steps secure
Put a new song in my mouth of praise to God
Many will see and fear
And put their trust in Him
Blessed is the man who makes the LORD his trust
Who does not turn to the proud
To those who go astray after lies


You have multiplied, O LORD my God
Your wondrous works and thoughts toward us
None can compare with You
I will proclaim Your wonders
Yet they’re more than can be told


He raised me up
He drew me out
He rescued me from the deepest and darkest pit
He raised me up
He drew me out
He reached His hand into the pit and raised me up
He raised me up


In sacrifice and offering
You’ve not delighted
But You’ve given me an open ear to hear
To hear and do Your will
Burnt offering and sin offering
You have not required
Then I said, “Behold, I have come
Within the scroll of the book it is written of me”


“I delight to do Your will, my God
Your law is within my heart”
I’ve told the happy news of deliverance among the saints
And I will tell it to everyone


I have not restrained my lips, O LORD
You know that I have not hidden Your deliverance within my heart
But I have spoken of Your faithfulness and salvation
I have not concealed Your love
Nor Your faithfulness from all the saints in the congregation


But as for you, O Yahweh
You won’t withhold Your love from me
Your steadfast love and Your faithfulness will always
And forever guard me


For evils without number have gathered around me
And my iniquities have overtaken me
So that I cannot see
They are more than the hairs of my head
My heart fails me
Let it be Your will to deliver me, O LORD
Come quickly to my aid
Be pleased to deliver me, O LORD
Make haste to help me
Let them be ashamed and disappointed
Who seek to snatch my life away
Let them be turned back and disgraced
Who delight in my hurt
Let them be humiliated by their shame
Who accuse me night and day


But may all who seek You rejoice and be glad in You
And may those who love Your salvation say, “Great is the Lord”
As for me, I’m poor and needy
But the LORD takes thought for me
Your my help and my deliverer
Do not delay, O my God


‘Cause You raised me up
You drew me out
You rescued me from the deepest and darkest pit
You raised me up
You drew me out
You reached Your hand into the pit and raised me up
You raised me up


(Psalm 40:1-17)

他把我拉起来


我曾切切等候着永恒主;
他倾向于我,听我的呼救。
他把我从毁灭坑里、
从淤泥中拉上,
使我的脚立在磐石上,
使我的步伐稳当。
他将新的歌赐与我口中,
就是颂赞我们之上帝的歌;
许多人看见,必起了敬畏的心,
来倚靠永恒主。


那将倚靠心寄托于永恒主的、
这人有福啊!
他不向慕狂傲之辈,
也不背离退落于虚谎。
永恒主我的上帝啊,
你奇妙的作为、
你行的许多大事、
你向我们所怀的意念、
没有什么能跟你并列的。
我要讲述或说明,
则多到不能数算。


宰祭和供物、你不喜爱;
你只为我挖通了耳朵好听从;
燔祭和解罪祭你也不要。
那时我说:『看哪,我来了——
经卷上已为我规定了——
我的上帝啊,我乐意行你的旨意;
你的律法都在我脏腑里呢。』


我要在多人大众中
将上帝救人之义气传为好消息;
我的嘴唇我必不制止;
永恒主啊,惟有你知道。
你救人之义气我未隐藏
于心中;
你的忠信和拯救
我已经说出;
你的坚爱和忠信
我未曾向多人大众隐瞒。


你呢、永恒主啊,求你也不要
向我制止你的慈怜;
愿你的坚爱和忠信
不断地守护我。
因为有祸患围绕着我
到不可胜数;
我的罪罚把我赶上了,
使我不能看见;
比我的头发还多,
以致我胆力尽失。


永恒主啊,请援救我哦;
永恒主啊,快帮助我哦。
愿那寻索我命要攫取的、
一概失望受辱;
愿那对我幸灾乐祸的、
退后狼狈。
愿那讥诮我说:『呵哈!呵哈!』的
都因失望而惊骇。


愿一切寻求你的、
都因你而高兴欢喜;
愿那些爱慕你的拯救的、
不断地说:『要尊永恒主为大。』
我,我困苦贫穷,
主仍顾念着我。
惟独你是帮助我解救我的;
我的上帝啊,不要迟延哦!


(诗篇 40:1-17 吕振中)

Read More

2023-02-12
In All the Earth


In All the Earth
by Emily Heilman


Yahweh, our Lord
How majestic is Your name
In all the earth
In all the earth
You have displayed Your glory upon the heavens


And from the mouths of children and infants
You have ordained praise
To confound Your enemies and silence them


When I consider Your heavens
When I consider Your heavens


When I consider Your heavens
The work of Your fingers
The moon and the stars You’ve hung in place


I think what is man, that You should think about him?
And who are we that You should care for us?


You have made us just a little lower than the angels
But You have crowned us with glory and honor
And You have made us the ruler of all the work of Your hands
You have placed all things under our feet
The flocks and the livestock
The beasts of the land
The birds of the air
The fish of the sea
And every creature of the ocean
O Lord, how majestic is Your name
In all the earth
In all the earth


Yahweh, our Lord
How majestic is Your name
In all the earth
In all the earth
In all the earth

永恒主我们的主啊,
你的名在全地多么庄严哪!


你将你的荣美摆设于天上。
你因你敌人的缘故、
从婴儿和吃奶者口中建立了能力,
使仇敌和报仇者销声息迹。


我观看你的天,你的指头所造的,
你所陈设的月亮和星辰;
啊,人算什么,你竟记挂着他?
世人算什么,你竟眷顾他?
你却使他稍微小于诸天神,
又将光荣尊贵做冠冕给他戴上。
你派他管理你手所造的,
使万物都服于他脚下:
使一切的羊和牛,
以及田野的兽,
空中的鸟和海里的鱼,
凡经行海道的,都服于他脚下。


永恒主我们的主啊,
你的名在全地多么庄严哪!


(诗篇 8:1-9 吕振中)

Read More

2023-02-12
Vindicate Me


Vindicate Me
by Nick Poppens


O LORD, my God, in You I place my trust
O LORD, my God, in You I place my trust
Rescue me from those who persecute me
And deliver me
Or they’ll devour my soul like a lion
Tearing me to pieces while my screams ring out in vain


O LORD, my God, if I have done what they say
If I’m the one in the wrong
If I have rewarded evil unto one who was at peace with me


Then let my enemies come and have their way with me
Let them take everything
Let them take their shots at me until I’m dead
And drag my name through the dirt
Selah


Arise, rise, rise, LORD, in Your anger
And wage, wage war against the anger of my enemies
Gather them into Your courts of justice
Ascend to Your judgment seat
And rain down justice from Your holy throne
Declare them guilty as they are
Acquit me of their charges and vindicate me
According to my innocence and my integrity


For the righteous God searches the mind and heart
He will establish the just and end all wickedness


And I have a shield from God who rescues those in the right
And every day His anger burns against the wicked ones
And if they will not turn around…


He has sharpened His sword
And He has bent back His bow
And taken aim with His arrows He set on fire
Because His foes are intent on evil
And they conceive nothing but worthless lies
And they have set a trap for me to fall into
But they will fall into the pit they made
Their evil will recoil and return to them
Their violence will come back around
And ruin them instead


And I will praise the LORD
According to His righteousness
And I will sing praise to the Most High

永恒主我的上帝啊,我避难于你里面;
求你拯救我脱离一切追赶我的人,
而抢救我;
恐怕他们像狮子撕裂我,
甚至撕碎,也无人抢救。


永恒主我的上帝啊,我若行了这事,
我手中若有不义,
我若以恶报那与我友好的人,
或无缘无故劫掠与我为敌的人,
那就任凭仇敌追赶我、直到追上,
将我的性命践踏于地上,
使我的光荣归于灰尘吧。
(细拉)


永恒主啊,求你以义忿起来,
挺身而立,抵挡我敌人的暴怒。
求你为我而奋发,
以行你所命定的审判。
愿万国之民的会环绕你;
而你临于其上,坐于高天。
永恒主为万族之民行裁判;
永恒主啊,求你按我的理直
按我心中的纯全为我申正义。


愿恶人的坏事绝止;
愿你使义人坚立着,
察验人心肠的
公义上帝啊。
我的盾牌是在于上帝,
那拯救心里正直的人的。
上帝是公义的审判者,
是天天向恶人发义怒的上帝。


人若不回转,
上帝必将他的刀磨快,
将他的弓拉紧,准备好好。
他为恶人豫备了致死的器械,
使他所射的、成为烧𤏲的箭。
看哪,恶人产痛地生了奸恶:
他所怀孕的是毒害,
所生的是虚假。
他掘了陷阱,给挖深了,
竟掉在自己所造的坑里。
他的毒害必回到他自己头上;
他的强暴必落到他自己头顶。


我要依照他的公义称谢永恒主,
唱扬至高者永恒主的名。


(诗篇 7:1-17 吕振中)

Read More

2023-02-10
Heal Me


Heal Me
by Deryck Box


LORD, do not rebuke me in Your anger
Nor chasten me in Your wrath
Be merciful to me, for I am weak and pining away


So heal me
Heal me
Heal me, Yahweh
Heal me
Heal me
Heal me, for my bones are dismayed


So heal me
Heal me
Heal me, Yahweh
Heal me
Heal me
Heal me, for my bones are dismayed


And my soul is greatly dismayed
But You, O LORD, how long?


Return, O LORD, deliver my soul
If you love me, then save me
‘Cause I cannot live for You if I waste away
Nor can I give You praise


So heal me
Heal me
Heal me, Yahweh
Heal me
Heal me
Heal me, for my bones are dismayed


I am worn out from groaning
All night and I flood my bed with tears
My eyes are wasting away with grief
Because of all my enemies


So heal me
Heal me
Heal me, Yahweh
Heal me
Heal me
Heal me, for my bones are dismayed


So heal me
Heal me
Heal me, Yahweh
Heal me
Heal me
Heal me, for my bones are dismayed


Depart, You enemies
For the LORD has heard my weeping
He will receive my prayer
Let all my enemies
Be ashamed and disappointed
Let them turn back and be suddenly defeated


So heal me
Heal me
Heal me, Yahweh
Heal me
Heal me
Heal me, for my bones are dismayed

永恒主啊,不要气哼哼责备我,
不要怒忿忿惩罚我。
永恒主啊,恩待我,因为我衰弱。
永恒主啊,医治我,因为我骨头惊得发颤;
我的心也非常惊惶。
但你呢、永恒主啊,要等到几时呢?


永恒主啊,回心转意,救拔我的性命哦;
因你坚爱的缘故拯救我哦。
因为在死地无人记念着你;
在阴间有谁称谢你?
我因叹息而困乏;
我每夜以眼泪使床漂浮着,
把床榻湿透。
我眼睛由愁苦而损坏;
又因我一切敌人而衰老昏花。


一切作孽之人哪,离开我吧;
因为永恒主听了我哭泣的声音。
永恒主听了我的恳求;
永恒主接受我的祷告。
我一切仇敌都必失望,非常惊惶;
他们必退后,一眨眼间便周章狼狈。


(诗篇 6:1-10 吕振中)

Read More

2023-02-09
Rejoice in You


Rejoice in You
by Chris Heesch


Give ear to my words, O LORD
Please hear my spirit groaning
Listen to the sound of my cry
To You, my God and King
In the morning, You’ll hear my voice as I offer my prayer
And I will watch for Your answer in eager expectation


For You are not a god who delights in sin
No evil dwells with You
And the prideful shall not stand
For You despise evil men
But as for me, I’ll come into Your house
Covered by Your abundant mercy
And in fear of You I’ll lay face down
Before Your throne in grateful worship


Oh, lead me in Your ways
Make Your path before me straight
Protect me from my enemies who attack me night and day


For their mouths are full of lies
And their hearts are wickedness
Their throats are an open grave
They trick many with their tongues
Convict them, O God
Let their rebellion lead to their fall
But for Your church, I pray
I pray…


Let all who trust in You be glad
Let them sing for joy all of their lives
For You guard them on every side
Let those in You rejoice in You
Surely You will bless Your own
You’ll defend them with Your steadfast love
With a shield the enemy can’t touch
Let those in You rejoice in You
For our enemies are strong, but You have overcome
So let Your people all over the earth exult in You

永恒主啊,侧耳听我说的话,
留意我所沉思的哦。
我的王我的上帝啊,
留心听我呼救的声音哦;
因为我是向你祈祷的。
永恒主啊,早晨你必听我的声音;
早晨我必向你陈明,来企望着。


因为你不是喜爱邪恶的上帝;
坏人不能寄居在你那里。
狂傲之人不能在你眼前站着;
作孽之人都是你所恨恶的。
说谎话的、你必杀灭;
好流人血弄诡诈的人
都是永恒主所厌恶的。


至于我呢、我必凭你坚爱之丰盛
进入你的殿;
我必存敬畏你的心
向你的圣殿堂敬拜。
永恒主啊,求你因那窥伺我者的缘故、
凭你的义气来引领我,
使你的道路在我面前平直。


因为他们口中都没有定性;
他们的脏腑满有毁灭;
他们的喉咙是敞开的坟墓;
他们用舌头谄媚人。
上帝啊,定他们为有罪;
愿他们由自己的计谋而败倒;
愿你因他们的许多过犯而驱逐他们;
因为他们悖逆了你。


但凡避难于你里面的、愿他们都喜乐,
永远欢呼;愿你掩护他们;
愿爱你名的人都靠着你而欢欣。
因为是你、永恒主啊,你赐福给义人;
你用恩悦如盾牌四面围护他。


(诗篇 5:1-12 吕振中)

Read More

2023-02-09
When I Call


When I Call
by Melissa Breems


Hear me as I call, O God
You′re all my righteousness
For You have rescued me
When I was drowning in the depths
Show favor again to me
And hear my prayer


How long, O sons of men, will you tear me down with guilt and shame?
How long will you love what’s worthless?
How long will your hearts chase after lies?


But know that God set me apart
And know that He has chosen me
And He will answer me
He will answer when I call


Pour out your complaints, but do not sin
Commune with Your heart upon your bed and be still
Offer up your sacrifice and trust in Him
For there are many who say, “Who will show us any good?”
Lord, show us just how good You are
Unveil Your glory


For You have put gladness in my heart
Even more than the wealthiest of earth
And I will lay me down, I will lay me down in peace
And I will sleep in Your peace
For You alone make me dwell in safety


I can rest because You are good
I can rest because You are faithful
I can rest because You are good to me

显我理直的上帝啊
我呼求时、求你应我。
我在困苦中、你曾给我宽绰;
现在求你恩待我,听我的祷告。


上流人哪,你们将我的光荣
变为侮辱、要到几时呢?
你们喜爱空洞,寻求虚谎、
要到几时呢?
(细拉)
要知道永恒主已分别坚贞之士归他自己
我向他呼求时、永恒主必听我。


你们尽管激动战抖,可别犯罪;
在床上时、要心中说话,静默无声。
(细拉)
要献对的祭,
要倚靠永恒主。


许多人总说:「谁能指示我们什么好处?」
永恒主啊,扬起你脸上的光来照我们哦。
你使我心里快乐,
胜过丰收了五谷新酒时的欢喜。
我必又平安、又躺下而睡觉,
因为只有永恒主、你能使我安然居住。


(诗篇 4:1-8 吕振中)

Read More

2023-02-09
Lifter of My Head


Lifter of My Head
by Daniel Brunz


LORD, so many are rising up against me
Many are they who say of my soul
There is no help for me in God


When they throw stones at me
I need not fear nor flee


For You, O LORD, are a shield for me
My glory, and the lifter of my head
For You, O LORD, are a shield for me
My glory, and the lifter of my head


I cried to the LORD with my voice
And He heard me, He heard me, He heard me
And I cried to the LORD with my voice
And He heard me, He heard me, He answered me


And I lay down and slept
And I awoke for Your presence sustained me


So when ten thousand fight me
I need not fear nor flee


So when ten thousand fight me
I need not fear nor flee


So when ten thousand fight me
I need not fear nor flee


For You, O LORD, are a shield for me
My glory, and the lifter of my head
For You, O LORD, are a shield for me
My glory, and the lifter of my head


Arise, O LORD, and save me, O my God
For You have struck my enemies
You have broken their teeth
For salvation comes from You alone
Your blessing is upon Your people forever

永恒主啊,我的敌人何其多呀!
有许多人起来攻击我。


有许多人议论我说:
『他是不能由上帝得救助的。』
(细拉)


但是你呢、永恒主啊,
你是我四围的盾牌;
你是我的光荣,是让我抬起头来的。
我出声呼求永恒主,
他从他的圣山上应了我。
(细拉)


我躺下,我睡觉,我醒来,
都因永恒主在扶持着我。
虽有千万族民周围排列攻击我,
我也不怕。


永恒主啊,求你起来;
我的上帝啊,拯救我哦;
因为你击打了我众仇敌的腮骨,
你敲碎了恶人的牙齿。


拯救属于永恒主;
愿你的祝福在你人民身上。
(细拉)


(诗篇 3:1-8 吕振中)

Read More