Skip to content

Instantly share code, notes, and snippets.

@ramtinak
Created March 20, 2025 14:45
Show Gist options
  • Save ramtinak/3a67a2af548e37e979f51deec87a69e9 to your computer and use it in GitHub Desktop.
Save ramtinak/3a67a2af548e37e979f51deec87a69e9 to your computer and use it in GitHub Desktop.
Instagram's Bloks Parser
/*
* 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