dilame / instagram-private-api

NodeJS Instagram private API SDK. Written in TypeScript.
MIT License
5.93k stars 1.14k forks source link

What happened to ig.account.logout()? #1472

Open LilMoke opened 3 years ago

LilMoke commented 3 years ago

I cannot find a way to log out.

My problem is, if I log in and post a story and then I call my function with an invalid password it still posts the story. I thought this may be because I do not logout after each post, but I cannot find how to logout.

If my issue is related to something else, any help would be appreciated.

seanconrad1 commented 3 years ago

It sounds like the ig object might still be holding a session. Would you be able to share your code?

LilMoke commented 3 years ago

Yes, that is why I thought logging out would release the session. I am not sure how to do that. Anyway, this is the code I am using:

var objectId = req.params.objectId
var socialServiceName = req.params.socialServiceName
var userName = req.params.userName
var password = req.params.password
console.log(`❌ ************************* PublishStory: ${objectId} with credentials: ${userName} ${password}`)

const Story = Parse.Object.extend("Story");
const query = new Parse.Query(Story);
try
{
    const story = await query.get(objectId)
    console.log(`❌ ************************* Story object retrieved successfully. story: ${JSON.stringify(story)}`);
    const ig = new IgApiClient();
    ig.state.generateDevice(userName)
    ig.state.proxyUrl = process.env.IG_PROXY;
    const loggedInUser = await ig.account.login(userName, password)
    console.log(`❌ ************************* loggedInUser: ${JSON.stringify(loggedInUser)}`)
    const imgAsset = await story.get("imageAsset");
    console.log(`❌ ************************* PublishStory imgAsset.url(): ${imgAsset.url()}`)
    const response = await Parse.Cloud.httpRequest(
    {
        url: imgAsset.url()
    })
    const resStory = await ig.publish.story(
    {
        file: response.buffer
    });
    return `${JSON.stringify(resStory)}`
}
catch (error)
{
    throw new Error(`Error: ${error}`);
}

I discovered this trying to code a function that would allow me to verify the user's instagram's credentials.

For that I was simply using this function, but never receive a failure message when changing the username or password:

var userName = req.params.userName
var password = req.params.password
console.log(`❌ ************************* testAccountLogin: with credentials: ${userName} ${password}`)

try
{
    const ig = new IgApiClient();
    ig.state.generateDevice(userName)
    ig.state.proxyUrl = process.env.IG_PROXY;
    const loggedInUser = await ig.account.login(userName, password)
    console.log(`❌ ************************* loggedInUser: ${JSON.stringify(loggedInUser)}`)
    return `${JSON.stringify(loggedInUser)}`
}
catch (error)
{
    throw new Error(`Error: ${error}`);
}

Thank you for the assistance.

seanconrad1 commented 3 years ago

So the session example helps a bit with that. You can use the code below to handle your session and use clearStateAndLogout()when you want to logout.

import { IgApiClient } from "instagram-private-api";
import fs from "fs";

const saveState = (data) => {
  fs.writeFileSync("./state.json", JSON.stringify(data));
  return data;
};

const checkState = () => {
  const data = fs.readFileSync("./state.json", "utf-8");
  if (data) {
    return true;
  }
  return false;
};

const loadState = () => {
  const value = fs.readFileSync("./state.json", "utf-8");
  return JSON.parse(value);
};

const clearStateAndLogout = async (ig) => {
  await ig.account.logout();
  const serialized = await ig.state.serialize();
  delete serialized.constants;
  saveState(serialized);
};

export const startApp = async () => {
  const ig = new IgApiClient();
  ig.state.generateDevice(process.env.USERNAME);

  // This function executes after every request
  ig.request.end$.subscribe(async () => {
    const serialized = await ig.state.serialize();
    delete serialized.constants; // this deletes the version info, so you'll always use the version provided by the library
    saveState(serialized);
  });

  if (checkState()) {
    await ig.state.deserialize(loadState());
  } else {
    try {
      await ig.account.login(process.env.USERNAME, process.env.PASSWORD);
    } catch (err) {
      console.log("Error logging into insta: ", err);
    }
  }

  //! Use this when you want to clear your state and logout
  await clearStateAndLogout(ig);

  // Your code here...
  const directInboxFeed = ig.feed.directInbox();
  const wholeResponse = await directInboxFeed.request();
  console.log(wholeResponse);
};

startApp();
DevStreamLine commented 3 years ago

So, this returns:

Error: ENOENT: no such file or directory, open './state.json'"

Do I need to create that file first so there is one available? It looks like it should create it at saveState

I basically have exactly what is above. It appears to be erroring out on this line:

ig.request.end$.subscribe(async () => {

so the saveState function never gets called.

Nerixyz commented 3 years ago

So the session example helps a bit with that. You can use the code below to handle your session and use clearStateAndLogout()when you want to logout.

import { IgApiClient } from "instagram-private-api";
import fs from "fs";

const saveState = (data) => {
  fs.writeFileSync("./state.json", JSON.stringify(data));
  return data;
};

const checkState = () => {
  const data = fs.readFileSync("./state.json", "utf-8");
  if (data) {
    return true;
  }
  return false;
};

const loadState = () => {
  const value = fs.readFileSync("./state.json", "utf-8");
  return JSON.parse(value);
};

You shouldn't mix synchronous with asynchronous code. readFileSync will block the event loop and thus block any request from being responded to. So you have to import fs from fs/promises.

For the checkState you can use fs.stat(...).then(() => true).catch(() => false). This won't throw.