Closed NoeKun001 closed 2 months ago
To get your bearer token on chrome based browers: -Go to prolific and login, -Press F12 -Go to the 'Network' Tab -filter by 'internal-api.prolific.com/api/v1/' -click F5 -click on any result and search on the "Request Headers" for something like
authorization: Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-copy it (eg Bearer JABSKJFBGDBGDBSKDBGKBSDGBSDGNB) -parse it (eg paste it into the address bar of your browser and recopy it) -your config.json should look something like this
{ "auto_renew_bearer" : "True", "pause_on_idle" : "False", "Prolific_ID" : "AAAAAAAAAAAAA", "proxy" : "http://username:password@server:port", "wait_time" : 12 }
-run -paste token
Notes:
if you dont use a proxy just leave it blank "proxy" : "",
pause on idle is not working, i dont actually use it
this is a frankenstein version, expect things to be janky and made in a dumb way
guessing the re-entry of the token when it expires wont work as well if idle_pause isnt working, so you will have to close the script and re run it.
import platform
import random
from argparse import ArgumentParser
from distutils.util import strtobool
from json import load
from pathlib import Path
from time import sleep, time
from webbrowser import open_new_tab
from playsound import playsound
from requests import Response, get, post
from requests.structures import CaseInsensitiveDict
from requests.exceptions import RequestException
from rich.console import Console
from rich.text import Text
if platform.system() == "Windows":
import ctypes
config = load(open(f"{Path(__file__).parent}/config/config.json"))
class ProlificUpdater:
def __init__(self, bearer, proxy=None):
self.bearer = bearer
self.proxy = proxy
self.oldResults = list()
self.participantId = config["Prolific_ID"]
self.last_ip = None
def getRequestFromProlific(self) -> Response:
url = "https://internal-api.prolific.com/api/v1/participant/studies/"
headers = CaseInsensitiveDict()
headers["Accept"] = "application/json, text/plain, */*"
headers["Authorization"] = self.bearer
headers["x-legacy-auth"] = "false"
proxies = {"http": self.proxy, "https": self.proxy} if self.proxy else None
try:
return get(url, headers=headers, proxies=proxies, timeout=10)
except RequestException as e:
console.print(f"[bold red]Request error: {e}[/bold red]")
return Response()
def reservePlace(self, id) -> Response:
url = "https://internal-api.prolific.com/api/v1/submissions/reserve/"
headers = CaseInsensitiveDict()
headers["Accept"] = "application/json"
headers["Authorization"] = self.bearer
headers["x-legacy-auth"] = "false"
postObj = {"study_id": id, "participant_id": self.participantId}
proxies = {"http": self.proxy, "https": self.proxy} if self.proxy else None
try:
return post(url, headers=headers, data=postObj, proxies=proxies)
except RequestException as e:
console.print(f"[bold red]Request error: {e}[/bold red]")
return Response()
def getResultsFromProlific(self) -> list:
try:
response = self.getRequestFromProlific()
except Exception:
console.print("[bold red][+] Network error[/bold red]")
return list()
if response.status_code == 200:
return response.json()["results"]
else:
print(f"Response error {response.status_code}")
print(f"Response error {response.reason}")
return ["bearer"]
def executeCycle(self) -> bool:
results = self.getResultsFromProlific()
if results == ["bearer"]:
self.bearer = input("Bearer token not valid anymore, please enter a new bearer token: ")
self.bearer = "Bearer " + self.bearer
return False
print("results : ", results)
if results:
if results != self.oldResults:
currency_symbol = "£" if str(results[0]["study_reward"]["currency"]) == "GBP" else "$"
console.print(
f"""Trying to join {results[0]["name"]} ([bold green]{currency_symbol}{str(float(results[0]["study_reward"]["amount"])/100)}[/bold green])"""
)
reserve_place_res = self.reservePlace(id=results[0]["id"])
if reserve_place_res.status_code == 400:
console.print(f"""[bold red][+] Error code {str(reserve_place_res.json()["error"]["error_code"])}
\nTitle : {str(reserve_place_res.json()["error"]["title"])}
\nDetails : {str(reserve_place_res.json()["error"]["detail"])}""")
self.oldResults = results
return False
else:
a_website = "https://app.prolific.com/studies"
open_new_tab(a_website)
playsound(rf"{Path(__file__).parent}\alert.wav", True)
self.oldResults = results
if results:
return True
else:
return False
def check_ip(self) -> str:
ip_services = [
'https://api.ipify.org?format=json',
'https://api.my-ip.io/ip.json',
'https://checkip.amazonaws.com',
'https://api.ipstack.com/check?access_key=YOUR_ACCESS_KEY'
]
for service in ip_services:
try:
response = get(service, proxies={"http": self.proxy, "https": self.proxy} if self.proxy else None)
if response.status_code == 200:
return response.json().get("ip", response.text.strip())
except RequestException:
continue
return "Unable to get IP"
def parseArgs() -> dict:
parser = ArgumentParser(description="Keep updated with Prolific")
parser.add_argument("-b", "--bearer", type=str, help="bearer token")
args = parser.parse_args()
bearer = "Bearer " + args.bearer if args.bearer else None
return {"bearer": bearer}
if platform.system() == "Windows":
class LASTINPUTINFO(ctypes.Structure):
_fields_ = [("cbSize", ctypes.c_uint), ("dwTime", ctypes.c_uint)]
def get_idle_duration():
lii = LASTINPUTINFO()
lii.cbSize = ctypes.sizeof(LASTINPUTINFO)
if ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lii)):
millis = ctypes.windll.kernel32.GetTickCount() - lii.dwTime
seconds = millis / 1000
return seconds
else:
error_code = (
ctypes.windll.kernel32.GetLastError()
) # Get the last error code
print(f"Error code: {error_code}")
return 0 # Handle the error as needed
def pause_script():
print("Pausing script...")
while True:
idle_duration = get_idle_duration()
if idle_duration > 600: # 10 minutes in seconds
print(
f"Idle Time: {idle_duration} seconds [script paused after 10 minutes of inactivity, to avoid temp ban from the API due to overuse]"
)
sleep(10)
else:
print("Resuming script...")
break
if __name__ == "__main__":
myArguments = parseArgs()
bearer = myArguments.get("bearer") or input("Please enter your bearer token: ")
bearer = "Bearer " + bearer
proxy = config.get("proxy")
p_updater = ProlificUpdater(bearer=bearer, proxy=proxy)
# Initial IP check
ip_address = p_updater.check_ip()
if ip_address == "Unable to get IP":
console = Console()
console.print(f"[bold red]Unable to get IP from all services. Stopping script.[/bold red]")
exit()
p_updater.last_ip = ip_address
console = Console()
console.print(f"Current IP address: {ip_address}")
status = console.status("[bold blue] Waiting for study...", spinner="arc")
status.start()
# Initial sleep
sleep(5)
while True:
updateTime = config["wait_time"]
if p_updater.executeCycle():
status.stop()
text = Text("Study found !")
text.stylize("bold red")
console.print(text)
sleep(5)
input("Press enter to resume study search")
status.start()
if strtobool(config["pause_on_idle"]) and platform.system() == "Windows":
idle_duration = get_idle_duration()
if idle_duration >= 600: # 10 minutes in seconds
pause_script()
# else:
# print(f"Idle Time: {idle_duration} seconds [script not paused]") #leave commented if not debugging
# IP check every 2 minutes
current_ip = p_updater.check_ip()
if current_ip != p_updater.last_ip:
console.print(f"[bold red]IP address has changed from {p_updater.last_ip} to {current_ip}. Stopping script.[/bold red]")
exit()
# Generate a random integer between -5 and 5
random_offset = random.randint(-1, 3)
# Generate a random number which is +/- 5 from the updateTime variable
random_time = updateTime + random_offset
sleep(
random_time if random_time >= 0 else 0
) # if statement covers edge case, if the user sets up <5 wait_time (I found <20 results in a temp ban), which with the use of random above, could result in a negative number being passed to the sleep()
Brother you are my greatest saviour... I pay homage to you! Glad you got it working brother, have fun.
Just a doubt... when will i have to change the bearer token though? (is there any general interval of time?) and suppose if the token expires while the script is running.... does it indicate me to enter other bearer token or just simply stops? it will say in the console "Bearer token not valid anymore, please enter a new bearer token: " at that point just close it and re run it
And can i also occasionally open prolififc studies on my browser (since im using the same bearer token... i think there might be some conflict) There is no conflict, just keep in mind it counts has a request (so the script is requesting every 12seconds for new studies and then you are requesting as well in browser)
Thank yuo bro <3
yeah i knew about that post, made me LOL hard.
ill keep it up so everyone that wants can use it. they had a captcha system before as well if I'm not mistaken. Currently I'm on 0 bans so I think I'm still "winning"
just restart the script with the same bearer token, all errors related to connection will say the bearer token is not valid even if it was just a network error
yes it will just not show Results: [] or give a timeout/response error the biggest tell will be that the prolific webpage on your browser will ask you to re-login since its the same session token. When it expires it will reflect on your normal prolific browser.
remove this lines or the script will sometimes randomly stop on you cause of checking for ip change and the return being different than your IP/being badly implemented.
'https://api.my-ip.io/ip.json',
'https://checkip.amazonaws.com',
'https://api.ipstack.com/check?access_key=YOUR_ACCESS_KEY'
Happy hunting!
avg income daily looks like its going to be within the avg many hours (6h++)
did some cleanup and some exceptions for slow connections/timeouts etc etc
import platform
import random
from argparse import ArgumentParser
from distutils.util import strtobool
from json import load
from pathlib import Path
from time import sleep, time
from webbrowser import open_new_tab
from playsound import playsound
from requests import Response, get, post
from requests.structures import CaseInsensitiveDict
from requests.exceptions import RequestException
from rich.console import Console
from rich.text import Text
if platform.system() == "Windows":
import ctypes
config = load(open(f"{Path(__file__).parent}/config/config.json"))
class ProlificUpdater:
def __init__(self, bearer, proxy=None):
self.bearer = bearer
self.proxy = proxy
self.oldResults = list()
self.participantId = config["Prolific_ID"]
self.last_ip = None
def getRequestFromProlific(self) -> Response:
url = "https://internal-api.prolific.com/api/v1/participant/studies/"
headers = CaseInsensitiveDict()
headers["Accept"] = "application/json, text/plain, */*"
headers["Authorization"] = self.bearer
headers["x-legacy-auth"] = "false"
proxies = {"http": self.proxy, "https": self.proxy} if self.proxy else None
try:
return get(url, headers=headers, proxies=proxies, timeout=10)
except RequestException as e:
console.print(f"[bold red]Request error: {e}[/bold red]")
return Response()
def reservePlace(self, id) -> Response:
url = "https://internal-api.prolific.com/api/v1/submissions/reserve/"
headers = CaseInsensitiveDict()
headers["Accept"] = "application/json"
headers["Authorization"] = self.bearer
headers["x-legacy-auth"] = "false"
postObj = {"study_id": id, "participant_id": self.participantId}
proxies = {"http": self.proxy, "https": self.proxy} if self.proxy else None
try:
return post(url, headers=headers, data=postObj, proxies=proxies)
except RequestException as e:
console.print(f"[bold red]Request error: {e}[/bold red]")
return Response()
def getResultsFromProlific(self) -> list:
try:
response = self.getRequestFromProlific()
except Exception:
console.print("[bold red][+] Network error[/bold red]")
return list()
if response.status_code == 200:
return response.json()["results"]
else:
print(f"Response error {response.status_code}")
print(f"Response error {response.reason}")
return ["bearer"]
def executeCycle(self) -> bool:
results = self.getResultsFromProlific()
if results == ["bearer"]:
self.bearer = input("Bearer token not valid anymore, please enter a new bearer token: ")
self.bearer = "Bearer " + self.bearer
return False
print("results : ", results)
if results:
if results != self.oldResults:
currency_symbol = "£" if str(results[0]["study_reward"]["currency"]) == "GBP" else "$"
console.print(
f"""Trying to join {results[0]["name"]} ([bold green]{currency_symbol}{str(float(results[0]["study_reward"]["amount"])/100)}[/bold green])"""
)
reserve_place_res = self.reservePlace(id=results[0]["id"])
if reserve_place_res.status_code == 400:
console.print(f"""[bold red][+] Error code {str(reserve_place_res.json()["error"]["error_code"])}
\nTitle : {str(reserve_place_res.json()["error"]["title"])}
\nDetails : {str(reserve_place_res.json()["error"]["detail"])}""")
self.oldResults = results
return False
else:
a_website = "https://app.prolific.com/studies"
open_new_tab(a_website)
playsound(rf"{Path(__file__).parent}\alert.wav", True)
self.oldResults = results
if results:
return True
else:
return False
def check_ip(self) -> str:
ip_services = [
'https://api.ipify.org?format=json',
'https://api.my-ip.io/ip.json',
'https://checkip.amazonaws.com',
'https://api.ipstack.com/check?access_key=YOUR_ACCESS_KEY'
]
for service in ip_services:
try:
response = get(service, proxies={"http": self.proxy, "https": self.proxy} if self.proxy else None)
if response.status_code == 200:
return response.json().get("ip", response.text.strip())
except RequestException:
continue
return "Unable to get IP"
def parseArgs() -> dict:
parser = ArgumentParser(description="Keep updated with Prolific")
parser.add_argument("-b", "--bearer", type=str, help="bearer token")
args = parser.parse_args()
bearer = "Bearer " + args.bearer if args.bearer else None
return {"bearer": bearer}
if platform.system() == "Windows":
class LASTINPUTINFO(ctypes.Structure):
_fields_ = [("cbSize", ctypes.c_uint), ("dwTime", ctypes.c_uint)]
def get_idle_duration():
lii = LASTINPUTINFO()
lii.cbSize = ctypes.sizeof(LASTINPUTINFO)
if ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lii)):
millis = ctypes.windll.kernel32.GetTickCount() - lii.dwTime
seconds = millis / 1000
return seconds
else:
error_code = (
ctypes.windll.kernel32.GetLastError()
) # Get the last error code
print(f"Error code: {error_code}")
return 0 # Handle the error as needed
def pause_script():
print("Pausing script...")
while True:
idle_duration = get_idle_duration()
if idle_duration > 600: # 10 minutes in seconds
print(
f"Idle Time: {idle_duration} seconds [script paused after 10 minutes of inactivity, to avoid temp ban from the API due to overuse]"
)
sleep(10)
else:
print("Resuming script...")
break
if __name__ == "__main__":
myArguments = parseArgs()
bearer = myArguments.get("bearer") or input("Please enter your bearer token: ")
bearer = "Bearer " + bearer
proxy = config.get("proxy")
p_updater = ProlificUpdater(bearer=bearer, proxy=proxy)
# Initial IP check
ip_address = p_updater.check_ip()
if ip_address == "Unable to get IP":
console = Console()
console.print(f"[bold red]Unable to get IP from all services. Stopping script.[/bold red]")
exit()
p_updater.last_ip = ip_address
console = Console()
console.print(f"Current IP address: {ip_address}")
status = console.status("[bold blue] Waiting for study...", spinner="arc")
status.start()
# Initial sleep
sleep(5)
while True:
updateTime = config["wait_time"]
if p_updater.executeCycle():
status.stop()
text = Text("Study found !")
text.stylize("bold red")
console.print(text)
sleep(5)
input("Press enter to resume study search")
status.start()
if strtobool(config["pause_on_idle"]) and platform.system() == "Windows":
idle_duration = get_idle_duration()
if idle_duration >= 600: # 10 minutes in seconds
pause_script()
# else:
# print(f"Idle Time: {idle_duration} seconds [script not paused]") #leave commented if not debugging
# IP check every 2 minutes
current_ip = p_updater.check_ip()
if current_ip != p_updater.last_ip:
console.print(f"[bold red]IP address has changed from {p_updater.last_ip} to {current_ip}. Stopping script.[/bold red]")
exit()
# Generate a random integer between -5 and 5
random_offset = random.randint(-1, 3)
# Generate a random number which is +/- 5 from the updateTime variable
random_time = updateTime + random_offset
sleep(
random_time if random_time >= 0 else 0
) # if statement covers edge case, if the user sets up <5 wait_time (I found <20 results in a temp ban), which with the use of random above, could result in a negative number being passed to the sleep()
thanks... Just one confirmation.. previously when the script ran it printed "results: []" and now it is "INFO:main:Results: []" right? and also how can i remove these (since i dont use proxy):
'https://api.my-ip.io/ip.json', 'https://checkip.amazonaws.com', 'https://api.ipstack.com/check?access_key=YOUR_ACCESS_KEY'
Thanks
Yes I added more verbose logging to check why my VPNs and Proxies were timming out in some accounts. (was not prolific, was my relay command server shitting itself)
I edited the above post to a version from today and that it has those ip checkers already removed and some more retry logic, either re-copy the above code or it would be remove lines 106 to 108 if you want to just remove that
After about 2 minutes it returns an error, is there a way to solve it other than giving it a new token every time?
use this version https://github.com/UnMars/Prolific-Joiner/issues/39#issuecomment-2289679551 use same logic to setup has here https://github.com/UnMars/Prolific-Joiner/issues/39#issuecomment-2289319012
use this version #39 (comment) use same logic to setup has here #39 (comment)
do i keep the website open?
use this version #39 (comment) use same logic to setup has here #39 (comment)
do i keep the website open?
its indifferent, just dont logout from prolific since that would probably void the token but you can even close your browser if you want to
--
today in the morning i adjusted my wait_time to be bigger than what i usually have... they seem to be doing adjustments on their side with the new login captcha and overall revamp of the timer etc.. more people than usual on reddit complaining about accounts on hold imo. to be safer than sorry i have a monitor with all accs studies page open on it in case something new like a captcha or a human-check appears after sometime being active is triggered etc. i highly doubt that since it would likely break their extention and it wasnt updated recently, but still they could do it on flagged accs only for example.. all my accs are still in good standing so its still not a auto-flag on the script i think
just wait and be on the lookout is what im doing, im still not doing a bypass on the captcha with 2captcha or something because they will probably continue and try and change things around until they are satisfied.
has i said my maneuver next couple of days is i changed the wait_time to be higher to compensate if they change rate limits/having the studies page open, i have the studies page open in case they insert something new, and added more error handling on the script (latest version i shared) to keep up with what is happening.
oh.. you are simultaneouly running the script (with longer time) + watching out the stuies page right? Dont they have the same toke? wont there be any conflict? and also could you please recommend the time range for us too (for the next couple days)... Thanks bro
yes exactly, MY guess is not currently, same has running 2 instances of the script.. you can do it but it will acheive nothing. just have in your mind the studies page refreshes every 60s (one request every 60s) and put your script with larger randomness and longer wait_time to compensate.. still thinkering but did something like (-2, 16) on the random variables and wait time at 22
probably will get some studies not reserved because of it but it is what it is.. also friday.
Maybe add that there will be a sound when the script stops because of an error
@ShaggyBro is it not possible to just put the token generated in the code? i did that and the script never told me that my token is not valid again...
@ShaggyBro is it not possible to just put the token generated in the code? i did that and the script never told me that my token is not valid again...
yes it should work untill it is invalid and ask for another/close the script
@ShaggyBro is it not possible to just put the token generated in the code? i did that and the script never told me that my token is not valid again...
yes it should work untill it is invalid and ask for another/close the script
so i placed in in those two places and it never tells that its not valid, i will monitor it and get to know if i am missing something. thanks for all your help brotherly
my easiest suggestion would be to just input it at the begining the one correlating to your browser.
explaining your doubt i would say imagine the bearer token has the login data stored (a sessionid, cookie). same way you can be logged into multiple devices at the same time, the same way multiple tokens can be valid.
@ShaggyBro Bro even the new script you provided is not working.... It gives an error (Reserve with a browser) Please help
Yeah I wonder if they changed stuff again, or is it a temporary error. Help would be nice.
Same, getting this:
Title : The data supplied is either incomplete or not valid.
PEC-SUB-0010: Sorry, we couldn't create your submission at this time. Please ensure that you are using a supported browser and that you have the latest version of Prolific. Try refreshing the page to resolve the issue.
Did anyone find a solution for the PEC-SUB-0010 error?
@profbiyi @notquitemoe @ShaggyBro @tommyka1 @UnMars did anyone know how to make this work?
As Far I can tell they changed captcha to Turnstile (Cloudflare), it can be bypassed by SeleniumBase UC Mode quite easily, however not sure if this solve PEC=SUB-0010.
As Far I can tell they changed captcha to Turnstile (Cloudflare), it can be bypassed by SeleniumBase UC Mode quite easily, however not sure if this solve PEC=SUB-0010.
mate could you please give me the code?
@ShaggyBro @UnMars any update please?
Error returns that the information is incomplete. I guess prolific changed their API so that it requires something more than a prolific ID to join a study. Someone will have to read documentation and figure out what it is. It failed me on multiple studies and was never able to join since this change happened
yes sir, my workaround currently was getting the token manually from prolific and start the script with it already inserted.