Created
March 20, 2025 14:45
-
-
Save ramtinak/3a67a2af548e37e979f51deec87a69e9 to your computer and use it in GitHub Desktop.
Instagram's Bloks Parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Developer: Ramtin Jokar [ [email protected] ] | |
* | |
* License MIT | |
* | |
*/ | |
using Newtonsoft.Json; | |
using System; | |
using System.Collections.Generic; | |
#pragma warning disable | |
namespace InstagramApiSharp.Helpers | |
{ | |
public class BloksParser | |
{ | |
private readonly string _input; | |
private int _index; | |
public BloksParser(string input) | |
{ | |
_input = input; | |
_index = 0; | |
} | |
public BloksNode Parse() | |
{ | |
SkipWhitespace(); | |
if (_input[_index] != '(') | |
{ | |
throw new Exception("Expected '(' at the start of the expression."); | |
} | |
_index++; | |
string operation = ReadOperation(); | |
BloksNode node = new BloksNode(operation); | |
while (_index < _input.Length && _input[_index] != ')') | |
{ | |
SkipWhitespace(); | |
if (_input[_index] == '(') | |
{ | |
node.AddParameter(Parse()); | |
} | |
else | |
{ | |
var parameter = ReadParameter(); | |
if (parameter?.ToString() != ",") // Skip commas | |
{ | |
node.AddParameter(parameter); | |
} | |
} | |
SkipWhitespace(); | |
} | |
if (_index >= _input.Length || _input[_index] != ')') | |
{ | |
throw new Exception("Expected ')' at the end of the expression."); | |
} | |
_index++; | |
return node; | |
} | |
private string ReadOperation() | |
{ | |
int start = _index; | |
while (_index < _input.Length && !char.IsWhiteSpace(_input[_index]) && _input[_index] != '(' && _input[_index] != ')') | |
{ | |
_index++; | |
} | |
return _input.Substring(start, _index - start); | |
} | |
private object ReadParameter() | |
{ | |
if (_input[_index] == '"') | |
{ | |
return ReadString(); | |
} | |
else if (char.IsDigit(_input[_index]) || _input[_index] == '-') | |
{ | |
return ReadNumber(); | |
} | |
else if (_input[_index] == 'n' && _input.Substring(_index, 4) == "null") | |
{ | |
_index += 4; | |
return null; | |
} | |
else | |
{ | |
return ReadSymbol(); | |
} | |
} | |
private string ReadString() | |
{ | |
_index++; | |
int start = _index; | |
while (_index < _input.Length && _input[_index] != '"') | |
{ | |
_index++; | |
} | |
string result = _input.Substring(start, _index - start); | |
_index++; | |
return result; | |
} | |
private object ReadNumber() | |
{ | |
int start = _index; | |
while (_index < _input.Length && (char.IsDigit(_input[_index]) || _input[_index] == '.' || _input[_index] == '-')) | |
{ | |
_index++; | |
} | |
string numberStr = _input.Substring(start, _index - start); | |
if (int.TryParse(numberStr, out int intResult)) | |
{ | |
return intResult; | |
} | |
else if (double.TryParse(numberStr, out double doubleResult)) | |
{ | |
return doubleResult; | |
} | |
throw new Exception("Invalid number format."); | |
} | |
private string ReadSymbol() | |
{ | |
int start = _index; | |
while (_index < _input.Length && !char.IsWhiteSpace(_input[_index]) && _input[_index] != '(' && _input[_index] != ')') | |
{ | |
_index++; | |
} | |
var symbol = _input.Substring(start, _index - start); | |
return symbol; | |
} | |
private void SkipWhitespace() | |
{ | |
while (_index < _input.Length && char.IsWhiteSpace(_input[_index])) | |
{ | |
_index++; | |
} | |
} | |
public object ConvertBlokNodeToJson(BloksNode node) | |
{ | |
if (node.Operation == "bk.action.map.Make,") | |
{ | |
var map = new Dictionary<string, object>(); | |
if (node.Parameters.Count == 2 && | |
node.Parameters[0] is BloksNode keysNode && keysNode.Operation == "bk.action.array.Make," && | |
node.Parameters[1] is BloksNode valuesNode && valuesNode.Operation == "bk.action.array.Make,") | |
{ | |
for (int i = 0; i < keysNode.Parameters.Count; i++) | |
{ | |
string key = keysNode.Parameters[i]?.ToString(); | |
if (!string.IsNullOrEmpty(key)) | |
{ | |
BloksNode value = (i < valuesNode.Parameters.Count) ? valuesNode.Parameters[i] as BloksNode : default; | |
if (value != null) | |
{ | |
var val = ConvertBlokNodeToJson(value); | |
map[key] = val; | |
} | |
else if (keysNode.Parameters.Count == valuesNode.Parameters.Count) | |
map[key] = valuesNode.Parameters[i]; | |
else | |
map[key] = value; | |
} | |
} | |
} | |
return map; | |
} | |
else if (node.Operation == "bk.action.array.Make,") | |
{ | |
var list = new List<object>(); | |
foreach (var param in node.Parameters) | |
{ | |
if (param is BloksNode childNode) | |
{ | |
list.Add(ConvertBlokNodeToJson(childNode)); | |
} | |
else | |
{ | |
list.Add(param); | |
} | |
} | |
return list; | |
} | |
return node.Parameters.Count == 1 ? node.Parameters[0] : node.Parameters; | |
} | |
public string ConvertToJsonString(BloksNode rootNode) | |
{ | |
object jsonObject = ConvertBlokNodeToJson(rootNode); | |
return JsonConvert.SerializeObject(jsonObject); | |
} | |
} | |
public class BloksNode | |
{ | |
public string Operation { get; set; } | |
public List<object> Parameters { get; set; } = new List<object>(); | |
public BloksNode(string operation) | |
{ | |
Operation = operation; | |
} | |
public void AddParameter(object parameter) | |
{ | |
Parameters.Add(parameter); | |
} | |
public override string ToString() | |
{ | |
return Operation; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment