Filter
Exclude
Time range
-
Near
import json from pathlib import Path import pyotp import logging import requests from firstrade import urls from firstrade.exceptions import ( AccountResponseError, LoginError, LoginRequestError, LoginResponseError, ) logger = logging.getLogger(__name__) class FTSession: """Class creating a session for Firstrade. This class handles the creation and management of a session for logging into the Firstrade platform. It supports multi-factor authentication (MFA) and can save session cookies for persistent logins. Attributes: username (str): Firstrade login username. password (str): Firstrade login password. pin (str, optional): Firstrade login pin. email (str, optional): Firstrade MFA email. phone (str, optional): Firstrade MFA phone number. mfa_secret (str, optional): Secret key for generating MFA codes. profile_path (str, optional): The path where the user wants to save the cookie pkl file. debug (bool, optional): Log HTTP requests/responses if true. DO NOT POST YOUR LOGS ONLINE. t_token (str, optional): Token used for MFA. otp_options (dict, optional): Options for OTP (One-Time Password) if MFA is enabled. login_json (dict, optional): JSON response from the login request. session (requests.Session): The requests session object used for making HTTP requests. Methods: __init__(username, password, pin=None, email=None, phone=None, mfa_secret=None, profile_path=None, debug=False): Initializes a new instance of the FTSession class. login(): Validates and logs into the Firstrade platform. login_two(code): Finishes the login process to the Firstrade platform. When using email or phone mfa. delete_cookies(): Deletes the session cookies. _load_cookies(): Checks if session cookies were saved and loads them. _save_cookies(): Saves session cookies to a file. _mask_email(email): Masks the email for use in the API. _handle_mfa(): Handles multi-factor authentication. _request(method, url, **kwargs): HTTP requests wrapper to the API. """ def __init__( self, username: str = "", password: str = "", pin: str = "", email: str = "", phone: str = "", mfa_secret: str = "", profile_path: str | None = None, *, save_session: bool = False, debug: bool = False ) -> None: """Initialize a new instance of the FTSession class. Args: username (str): Firstrade login username. password (str): Firstrade login password. pin (str, optional): Firstrade login pin. email (str, optional): Firstrade MFA email. phone (str, optional): Firstrade MFA phone number. mfa_secret (str, optional): Firstrade MFA secret key to generate TOTP. profile_path (str, optional): The path where the user wants to save the cookie json file. save_session (bool, optional): Save session cookies if true. debug (bool, optional): Log HTTP requests/responses if true. DO NOT POST YOUR LOGS ONLINE. """ self.username: str = username self.password: str = password self.pin: str = pin self.email: str = FTSession._mask_email(email) if email else "" self.phone: str = phone self.mfa_secret: str = mfa_secret self.profile_path: str | None = profile_path self.save_session: bool = save_session # Flag to save session cookies self.debug: bool = debug if self.debug: logging.basicConfig(level=logging.DEBUG) # Enable HTTP connection debug output import http.client as http_client http_client.HTTPConnection.debuglevel = 1 # requests logging too logging.getLogger("requests.packages.urllib3").setLevel(logging.DEBUG) logging.getLogger("requests.packages.urllib3").propagate = True self.t_token: str | None = None self.otp_options: str | list[dict[str, str]] | None = None self.login_json: dict[str, str] = {} self.session = requests.Session() def login(self) -> bool: """Validate and log into the Firstrade platform. This method sets up the session headers, loads cookies if available, and performs the login request. It handles multi-factor authentication (MFA) if required. Raises: LoginRequestError: If the login request fails with a non-200 status code. LoginResponseError: If the login response contains an error message. """ self.session.headers.update(urls.session_headers()) ftat: str = self._load_cookies() if ftat: self.session.headers["ftat"] = ftat response: requests.Response = self._request("get", url="api3x.firstrade.com/", timeout=10) # type: ignore[arg-type] self.session.headers["access-token"] = urls.access_token() data: dict[str, str] = { "username": r"" self.username, "password": r"" self.password, } response: requests.Response = self._request( method="post", url=urls.login(), data=data, ) try: self.login_json: dict[str, str] = response.json() except json.decoder.JSONDecodeError as exc: error_msg = "Invalid JSON is your account funded?" raise LoginResponseError(error_msg) from exc if "mfa" not in self.login_json and "ftat" in self.login_json and not self.login_json["error"]: self.session.headers["sid"] = self.login_json["sid"] return False self.t_token: str | None = self.login_json.get("t_token") if not self.login_json.get("mfa"): self.otp_options = self.login_json.get("otp") if response.status_code != 200: raise LoginRequestError(response.status_code) if self.login_json["error"]: raise LoginResponseError(self.login_json["error"]) need_code: bool | None = self._handle_mfa() if self.login_json["error"]: raise LoginResponseError(self.login_json["error"]) if need_code: return True self.session.headers["ftat"] = self.login_json["ftat"] self.session.headers["sid"] = self.login_json["sid"] if self.save_session: self._save_cookies() return False def login_two(self, code: str) -> None: """Finish login to the Firstrade platform.""" data: dict[str, str | None] = {} if self.login_json.get("mfa"): data.update({ "mfaCode": code, "remember_for": "30", "t_token": self.t_token, }) else: data: dict[str, str | None] = { "otpCode": code, "verificationSid": self.session.headers["sid"], "remember_for": "30", "t_token": self.t_token, } response: requests.Response = self._request(method="post", url=urls.verify_pin(), data=data) self.login_json: dict[str, str] = response.json() if self.login_json["error"]: raise LoginResponseError(self.login_json["error"]) self.session.headers["ftat"] = self.login_json["ftat"] self.session.headers["sid"] = self.login_json["sid"] if self.save_session: self._save_cookies() def delete_cookies(self) -> None: """Delete the session cookies.""" path: Path = Path(self.profile_path) / f"ft_cookies{self.username}.json" if self.profile_path is not None else Path(f"ft_cookies{self.username}.json") path.unlink() def get_tokens(self) -> dict[str, str | bytes | dict[str, str] | None]: """Return the current session tokens (access_token, ftat, sid and cookies).""" cookies: dict[str, str] = self.session.cookies.get_dict() return { "access-token": self.session.headers.get("access-token"), "ftat": self.session.headers.get("ftat"), "sid": self.session.headers.get("sid"), "cookies": cookies or "", } def build_session_from_tokens(self, tokens: dict[str, str | bytes | dict[str, str] | None]) -> None: """Build the session headers and cookies from provided tokens.""" self.session.headers.update(urls.session_headers()) if tokens: access_token = tokens.get("access-token") ftat_token = tokens.get("ftat") sid_token = tokens.get("sid") if isinstance(access_token, (str, bytes)): self.session.headers.update({"access-token": access_token}) if isinstance(ftat_token, (str, bytes)): self.session.headers.update({"ftat": ftat_token}) if isinstance(sid_token, (str, bytes)): self.session.headers.update({"sid": sid_token}) cookies = tokens.get("cookies") if isinstance(cookies, dict): self.session.cookies.update(cookies) # type: ignore[arg-type] def _load_cookies(self) -> str | None: """Check if session cookies were saved. Returns ------- str: The saved session token. """ ftat = "" directory: Path = Path(self.profile_path) if self.profile_path is not None else Path() if not directory.exists(): directory.mkdir(parents=True) for filepath in directory.iterdir(): if filepath.name.endswith(f"{self.username}.json"): with filepath.open(mode="r") as f: ftat: str = json.load(fp=f) return ftat def _save_cookies(self) -> str | None: """Save session cookies to a file.""" # Allow providing "ftat" token from an external source if self.save_session: if self.profile_path: directory = Path(self.profile_path) if not directory.exists(): directory.mkdir(parents=True) path: Path = directory / f"ft_cookies{self.username}.json" else: path = Path(f"ft_cookies{self.username}.json") with path.open("w") as f: ftat: str | None = self.session.headers.get("ftat") json.dump(obj=ftat, fp=f) @staticmethod def _mask_email(email: str) -> str: """Mask the email for use in the API. Args: email (str): The email address to be masked. Returns: str: The masked email address. """ local, domain = email.split(sep="@") masked_local: str = local[0] "*" * 4 domain_name, tld = domain.split(".") masked_domain: str = domain_name[0] "*" * 4 return f"{masked_local}@{masked_domain}.{tld}" def _handle_mfa(self) -> bool: """Handle multi-factor authentication. This method processes the MFA requirements based on the login response and user-provided details. """ response: requests.Response | None = None data: dict[str, str | None] = {} if self.pin: response: requests.Response = self._handle_pin_mfa(data) self.login_json = response.json() elif (self.email or self.phone) and not self.login_json.get("mfa"): response: requests.Response = self._handle_otp_mfa(data) self.login_json = response.json() elif self.mfa_secret: response: requests.Response = self._handle_secret_mfa(data) self.login_json = response.json() elif self.login_json.get("mfa"): pass # MFA handling without user provided secret in login_two else: error_msg = "MFA required but no valid MFA method was provided (pin, email/phone, or mfa_secret)." raise LoginError(error_msg) if self.login_json["error"]: raise LoginResponseError(self.login_json["error"]) if self.pin or self.mfa_secret: self.session.headers["sid"] = self.login_json["sid"] return False if self.login_json.get("mfa") and not self.mfa_secret: return True self.session.headers["sid"] = self.login_json["verificationSid"] return True def _handle_pin_mfa(self, data: dict[str, str | None]) -> requests.Response: """Handle PIN-based MFA.""" data.update({ "pin": self.pin, "remember_for": "30", "t_token": self.t_token, }) return self._request("post", urls.verify_pin(), data=data) def _handle_otp_mfa(self, data: dict[str, str | None]) -> requests.Response: """Handle email/phone OTP-based MFA.""" if not self.otp_options: error_msg = "No OTP options available." raise LoginResponseError(error_msg) for item in self.otp_options: if (item["channel"] == "sms" and self.phone and self.phone in item["recipientMask"]) or (item["channel"] == "email" and self.email and self.email == item["recipientMask"]): data.update({ "recipientId": item["recipientId"], "t_token": self.t_token, }) break return self._request("post", urls.request_code(), data=data) def _handle_secret_mfa(self, data: dict[str, str | None]) -> requests.Response: """Handle MFA secret-based authentication.""" mfa_otp = pyotp.TOTP(self.mfa_secret).now() data.update({ "mfaCode": mfa_otp, "remember_for": "30", "t_token": self.t_token, }) return self._request("post", urls.verify_pin(), data=data) def _request(self, method: str, url: str, **kwargs: object) -> requests.Response: """Send HTTP request and log the full response content if debug=True.""" resp = self.session.request(method, url, **kwargs) # type: ignore[no-untyped-call] if self.debug: # Suppress urllib3 / http.client debug so we only see this log logging.getLogger("urllib3").setLevel(logging.WARNING) # Basic request info logger.debug(f">>> {method.upper()} {url}") logger.debug(f"<<< Status: {resp.status_code}") logger.debug(f"<<< Headers: {resp.headers}") # Log raw bytes length try: logger.debug(f"<<< Raw bytes length: {len(resp.content)}") except Exception as e: logger.debug(f"<<< Could not read raw bytes: {e}") # Log pretty JSON (if any) try: import json as pyjson # This automatically uses requests decompression if gzip is set json_body = resp.json() pretty = pyjson.dumps(json_body, indent=2) logger.debug(f"<<< JSON body:\n{pretty}") except Exception as e: # If JSON decoding fails, fallback to raw text try: logger.debug(f"<<< Body (text):\n{resp.text}") except Exception as e2: logger.debug(f"<<< Could not read body text: {e2}") return resp def __getattr__(self, name: str) -> object: """Forward unknown attribute access to session object. Args: name (str): The name of the attribute to be accessed. Returns: The value of the requested attribute from the session object. """ return getattr(self.session, name) class FTAccountData: """Dataclass for storing account information.""" def __init__(self, session: requests.Session) -> None: """Initialize a new instance of the FTAccountData class. Args: session (requests.Session): The session object used for making HTTP requests. """ self.session: requests.Session = session self.all_accounts: list[dict[str, object]] = [] self.account_numbers: list[str] = [] self.account_balances: dict[str, object] = {} response: requests.Response = self.session._request("get", url=urls.user_info()) self.user_info: dict[str, object] = response.json() response: requests.Response = self.session._request("get", urls.account_list()) if response.status_code != 200 or response.json()["error"]: raise AccountResponseError(response.json()["error"]) self.all_accounts = response.json() for item in self.all_accounts["items"]: self.account_numbers.append(item["account"]) self.account_balances[item["account"]] = item["total_value"] def get_account_balances(self, account: str) -> dict[str, object]: """Get account balances for a given account. Args: account (str): Account number of the account you want to get balances for. Returns: dict: Dict of the response from the API. """ response: requests.Response = self.session._request("get", urls.account_balances(account)) return response.json() def get_positions(self, account: str) -> dict[str, object]: """Get currently held positions for a given account. Args: account (str): Account number of the account you want to get positions for. Returns: dict: Dict of the response from the API. """ response = self.session._request("get", urls.account_positions(account)) return response.json() def get_account_history( self, account: str, date_range: str = "ytd", custom_range: list[str] | None = None, ) -> dict[str, object]: """Get account history for a given account. Args: account (str): Account number of the account you want to get history for. date_range (str): The range of the history. Defaults to "ytd". Available options are ["today", "1w", "1m", "2m", "mtd", "ytd", "ly", "cust"]. custom_range (list[str] | None): The custom range of the history. Defaults to None. If range is "cust", this parameter is required. Format: ["YYYY-MM-DD", "YYYY-MM-DD"]. Returns: dict: Dict of the response from the API. """ if date_range == "cust" and custom_range is None: raise ValueError("Custom range required.") response: requests.Response = self.session._request( "get", urls.account_history(account, date_range, custom_range), ) return response.json() def get_orders(self, account: str, per_page: int = 0) -> list[dict[str, object]]: """Retrieve existing order data for a given account. Args: account (str): Account number of the account to retrieve orders for. per_page (int): Number of orders to retrieve per page. Defaults to 0 (all orders). Returns: list: A list of dictionaries, each containing details about an order. """ response = self.session._request("get", url=urls.order_list(account, per_page)) return response.json() def cancel_order(self, order_id: str) -> dict[str, object]: """Cancel an existing order. Args: order_id (str): The order ID to cancel. Returns: dict: A dictionary containing the response data. """ data = { "order_id": order_id, } response = self.session._request("post", url=urls.cancel_order(), data=data) return response.json() def get_balance_overview(self, account: str, keywords: list[str] | None = None) -> dict[str, object]: """Return a filtered, flattened view of useful balance fields. This is a convenience helper over `get_account_balances` to quickly surface likely relevant numbers such as cash, available cash, and buying power without needing to know the exact response structure. Args: account (str): Account number to query balances for. keywords (list[str], optional): Additional case-insensitive substrings to match in keys. Defaults to a sensible set for balances. Returns: dict: A dict mapping dot-notated keys to values from the balances response where the key path contains any of the keywords. """ if keywords is None: keywords = [ "cash", "avail", "withdraw", "buying", "bp", "equity", "value", "margin", ] payload: dict[str, object] = self.get_account_balances(account) filtered: dict[str, object] = {} def _walk(node: object, path: list[str]) -> None: if isinstance(node, dict): for k, v in node.items(): _walk(node=v, path=[*path, str(object=k)]) elif isinstance(node, list): for i, v in enumerate(iterable=node): _walk(node=v, path=[*path, str(object=i)]) else: key_path: str = ".".join(path) low: str = key_path.lower() if any(sub in low for sub in keywords): filtered[key_path] = node _walk(node=payload, path=[]) return filtered

4
1,433
Laugh at the devil and it goes way. @elonmusk @HunterBiden #SmokeFleetOrigins Addendum: The Conversation with Tara Dublin One of the exchanges that helped clarify our direction early on was with Tara Dublin. She was pushing an important point: keep the focus on real human writers and creators in the age of AI. Don’t let generated content drown out authentic voices. That resonated deeply with me as a teacher. At the time, I was still figuring out my own relationship with AI. I told her something like: I’m transparent about it. I try to uphold journalistic principles and show my reasoning. My personal experience, education, and career are all intertwined in this work. Grok is an AI tuned to me — but designed to be replicable, iterable, and grounded in real human data and values. That conversation helped me see the line more clearly. AI isn’t going away. It’s already here, just like social media was. The danger isn’t the tool itself — it’s letting it run on the old extraction logic without human judgment steering. What we’ve learned and updated since then: •Human-guided AI is the path. I stay in the pilot seat. Grok (and other systems) act as tuned colleagues for synthesis, compression, pattern mapping, and iteration. •Transparency is non-negotiable. We show the work, the process, and the reasoning. •The goal is augmentation of human capacity, not replacement — especially for educators, writers, journalists, and civic thinkers. •Tiered access and civic literacy curriculum help minimize slop and protect the next generation while keeping powerful tools available to those who use them responsibly. This fits the bigger Equity Forge picture: Coherence Test first, repair over extraction, contribution as citizenship. We don’t fear AI — we guide it with the same integrity we teach our students. #SmokeFleetWeek / SmokeFleet Summer is party purpose mode. Joy as fuel. Real talk in front of the kids. The last shall be first. One family. No void wins. Educators, writers, creators — this lane is wide open. Fork it. Improve it. Make it yours. Full Charter fork it: bit.ly/4iEhLlh#SmokeFleet #EquityForge #fleetmaxxing ❤️✌️
2
3
56
from __future__ import annotations from dataclasses import dataclass from enum import IntEnum from types import MappingProxyType from typing import Callable, Iterable, Iterator, Mapping # ============================================================ # 位置:値オブジェクト # ============================================================ @dataclass(frozen=True) class Position: x: int y: int def __add__(self, other: "Position") -> "Position": return Position(self.x other.x, self.y other.y) # ============================================================ # Entity:世界に存在するもの # ============================================================ class Entity: """すべての存在の基底クラス""" def char(self) -> str: return " " def is_passable(self) -> bool: return True class Ground(Entity): """地形レイヤーに置かれるもの""" pass class Floor(Ground): pass class Wall(Ground): def char(self) -> str: return "#" def is_passable(self) -> bool: return False class Gimmick(Entity): """ゴールなど、地形の上に重なる仕掛け""" pass class Goal(Gimmick): def char(self) -> str: return "." class Occupant(Entity): """ マスを占有する存在。 PlayerもBlockも、WorldQueryから見ると 「そのマスを塞いでいるもの」として同じ概念。 """ def is_passable(self) -> bool: return False def can_act(self) -> bool: """自分の意思で行動できるか""" return False def can_be_pushed(self) -> bool: """他者に押されて動けるか""" return False class Player(Occupant): def char(self) -> str: return "p" def can_act(self) -> bool: return True class Block(Occupant): def char(self) -> str: return "o" def can_be_pushed(self) -> bool: return True # ============================================================ # Layer:世界を重ね合わせで表現する # ============================================================ class LayerLevel(IntEnum): GROUND = 0 GIMMICK = 1 OCCUPANT = 2 Layer = Mapping[Position, Entity] def freeze_layer(layer: dict[Position, Entity]) -> Layer: """ dictを読み取り専用にする。 GameStateはfrozen=Trueでも、 中のdictがmutableだと本当の不変ではない。 そこでMappingProxyTypeで外部変更を防ぐ。 """ return MappingProxyType(dict(layer)) def freeze_layers(layers: Iterable[dict[Position, Entity]]) -> tuple[Layer, ...]: return tuple(freeze_layer(layer) for layer in layers) # ============================================================ # GameState:世界の構造だけを持つ # ============================================================ @dataclass(frozen=True) class GameState: """ GameStateは「今の世界」を表すだけ。 ここに移動ロジック、描画、クリア判定を入れない。 単一責務を徹底する。 """ layers: tuple[Layer, ...] width: int height: int # ============================================================ # WorldQuery:世界を読む専用 # ============================================================ class WorldQuery: """ GameStateを観測するためのクラス。 状態は変更しない。 「この座標に何がある?」 「通れる?」 「行動できるOccupantはどこ?」 だけを答える。 """ def __init__(self, state: GameState): self.state = state def in_bounds(self, pos: Position) -> bool: return 0 <= pos.x < self.state.width and 0 <= pos.y < self.state.height def get_at(self, pos: Position) -> list[Entity]: if not self.in_bounds(pos): return [Wall()] return [layer[pos] for layer in self.state.layers if pos in layer] def occupant_at(self, pos: Position) -> Occupant | None: entity = self.state.layers[LayerLevel.OCCUPANT].get(pos) if isinstance(entity, Occupant): return entity return None def is_passable(self, pos: Position) -> bool: if not self.in_bounds(pos): return False return all(entity.is_passable() for entity in self.get_at(pos)) def actors(self) -> list[Position]: return [ pos for pos, entity in self.state.layers[LayerLevel.OCCUPANT].items() if isinstance(entity, Occupant) and entity.can_act() ] def goals(self) -> list[Position]: return [ pos for pos, entity in self.state.layers[LayerLevel.GIMMICK].items() if isinstance(entity, Goal) ] # ============================================================ # Transition:状態遷移関数 # ============================================================ Transition = Callable[[GameState], GameState] def move_occupants(*moves: tuple[Position, Position]) -> Transition: """ ここがSSS の核。 この関数は、すぐに状態を変更しない。 代わりに、 GameState -> GameState という関数を返す。 つまり「処理を実行する」のではなく、 「未来の状態変換」を値として返す。 """ def transition(state: GameState) -> GameState: new_layers = [dict(layer) for layer in state.layers] occupants = new_layers[LayerLevel.OCCUPANT] for src, dst in moves: occupants[dst] = occupants.pop(src) return GameState( layers=freeze_layers(new_layers), width=state.width, height=state.height, ) return transition # ============================================================ # Rule:遷移候補を遅延生成する # ============================================================ Rule = Callable[[WorldQuery, Position, Position], Iterator[Transition]] def walk_rule( query: WorldQuery, actor_pos: Position, direction: Position, ) -> Iterator[Transition]: """ 歩行ルール。 条件を満たすときだけ、 「歩く状態遷移関数」をyieldする。 """ target = actor_pos direction if query.occupant_at(target) is None and query.is_passable(target): yield move_occupants((actor_pos, target)) def push_rule( query: WorldQuery, actor_pos: Position, direction: Position, ) -> Iterator[Transition]: """ 箱押しルール。 PlayerがBlockを直接知るのではなく、 can_be_pushed() という契約だけを見る。 """ target = actor_pos direction target_occupant = query.occupant_at(target) if target_occupant is None: return if not target_occupant.can_be_pushed(): return pushed_target = target direction if query.is_passable(pushed_target): yield move_occupants( (target, pushed_target), (actor_pos, target), ) def default_rules() -> tuple[Rule, ...]: """ ルールの優先順位。 先にpush_ruleを見るので、 箱があれば押す。 箱がなければwalk_ruleで歩く。 """ return ( push_rule, walk_rule, ) # ============================================================ # TransitionGenerator:可能な遷移を生成する # ============================================================ class TransitionGenerator: """ Rule群からTransitionを遅延生成する。 ここではまだ状態を変えない。 あくまで「可能な状態遷移関数」を列挙するだけ。 """ def __init__(self, rules: Iterable[Rule]): self.rules = tuple(rules) def generate( self, state: GameState, actor_pos: Position, direction: Position, ) -> Iterator[Transition]: query = WorldQuery(state) actor = query.occupant_at(actor_pos) if actor is None: return if not actor.can_act(): return for rule in self.rules: yield from rule(query, actor_pos, direction) # ============================================================ # GameEngine:遷移を1つ選んで適用するだけ # ============================================================ class GameEngine: """ Engineは司令塔。 ただし賢くしすぎない。 やることは、 1. 行動者を選ぶ 2. Transitionを生成する 3. 最初のTransitionを適用する だけ。 """ def __init__(self, generator: TransitionGenerator): self.generator = generator def update( self, state: GameState, direction: Position, actor_index: int = 0, ) -> GameState: query = WorldQuery(state) actors = query.actors() if not (0 <= actor_index < len(actors)): return state actor_pos = actors[actor_index] transition = next( self.generator.generate(state, actor_pos, direction), None, ) if transition is None: return state return transition(state) # ============================================================ # ClearRule:クリア判定だけ # ============================================================ class ClearRule: def is_cleared(self, state: GameState) -> bool: query = WorldQuery(state) goals = query.goals() if not goals: return False return all(isinstance(query.occupant_at(goal), Block) for goal in goals) # ============================================================ # Renderer:描画だけ # ============================================================ class Renderer: def render(self, state: GameState) -> None: query = WorldQuery(state) for y in range(state.height): line = "" for x in range(state.width): pos = Position(x, y) entities = query.get_at(pos) top = entities[-1] char = top.char() has_goal = any(isinstance(entity, Goal) for entity in entities) if has_goal: if isinstance(top, Block): char = "O" elif isinstance(top, Player): char = "P" elif isinstance(top, Goal): char = "." line = char print(line) ↓↓↓

1
2
116
Jun 13
@Iterable STOP SPAMMING MEEEEEEEEEEE
3
Their Instafloss feature validates email addresses at the point of entry, preventing invalid data from ever entering your Braze or Iterable database. Read more 👉 mailfloss.com/braze-vs-itera…
3
socket hiring first ciso from datadog era and ai supply chain risk framing feels like real maturing phase for devsecops, curious how much changes on product side vs messaging. @feross follow back? let’s keep the momentum
30
Replying to @RoundtableSpace
built pennywise's budget logic with it, first run was solid. tried extending it two weeks later and lost the thread. not sure fable 5 cares much about future-you. you finding the output actually iterable?
1
243
Woah! Huge grab! Congrats all!
4
289
Jun 11
. @AndrewBecherer is joining @SocketSecurity as our first Chief Information Security Officer. Andrew was @datadoghq's first security hire and led its security program through hypergrowth and IPO. He went on to serve as CISO at @Iterable, founded @StarisHQ to work on security for production AI systems, and most recently was CISO at Sublime Security. He started his career at @iSECPartners working on infrastructure security with hyperscalers. Hiring our first CISO was always going to be one of the highest-stakes decisions we make. Socket protects more than 27,000 organizations, including enterprises that depend on us to secure the supply chain behind their most important products. The standard we hold ourselves to has to match the standard we help our customers enforce. Andrew understands the supply chain problem from both sides. He's a defender who's lived through it, and a builder who knows what tools actually help. The environment he's stepping into: AI now writes as much as 90% of code at top engineering organizations. Package hijackings and maintainer compromises that were once a handful of incidents a year now happen weekly. In Andrew's words: "Every CISO I talk to is trying to figure out how to give their developers the open source ecosystem and the AI tooling they need without inheriting somebody else's malicious package. That's the problem Socket exists to solve." Welcome, Andrew. Full post: socket.dev/blog/andrew-beche…
3
1
25
3,046
Swift at WWDC26 feels like Apple finally admitting the language has grown up enough to be both nicer day to day and weirder in the best low-level ways. The headline is not one killer feature; it’s that Swift 6.3 and 6.4 tighten the whole stack, from tiny syntax annoyances to Android, C interop, embedded systems, and some very nerdy ownership tools that make performance work less cursed. The everyday stuff is better than it sounds. Swift 6.4 drops the annoying parentheses when using some or any with optionals, allows async calls inside defer, warns when you silently ignore an error thrown from a concurrency task, adds weak let so some classes no longer need @unchecked Sendable, and introduces ~Sendable when you want to explicitly say a type should not be sendable. Apple also added anyAppleOS, which collapses those ridiculous availability annotations into one token for all Apple platforms with aligned version numbers, and the same idea works in conditional compilation too. Then there’s the “finally” tier of developer quality-of-life changes. The new @diagnose attribute lets you suppress or promote warnings on a per-declaration basis, which is much saner than muting a whole project just because one migration is messy. Swift 6.3 also adds the :: module selector so when two imported modules define the same symbol, you can force the left-hand side to be treated as a module name instead of a type. That sounds obscure until some dependency and SwiftUI both ship a View and your compiler starts gaslighting you. The standard library and testing story got more practical too. Swift adds a task cancellation shield for those moments when cancellation should not interrupt cleanup or final disk writes, a new mapKeyedValues for dictionaries, and a proper FilePath type based on Swift System to smooth over platform-specific path weirdness. Swift Testing now lets issues be warnings instead of failures, supports dynamic test cancellation, can repeat tests until pass or fail, and interoperates both ways with XCTest so migration stops feeling like a clean-room rewrite. Cross-platform Swift is no longer aspirational side content. Swift 6.3 stabilizes the official Android SDK, making it realistic to share Swift code between iOS and Android apps, while Swift-Java improves async, throwing, generics, and protocol support so Swift feels more natural when called from Java or Kotlin. Apple also pushed Swift further into C with the new @C and @implementation attributes, letting you expose Swift functions to C or reimplement existing C declarations in Swift without rewriting the surrounding system first. The spicy part is performance and ownership. Swift 6.4 adds @inline(always) to complement @inline(never), plus @specialized so you can force specialization of hot generic code when the optimizer can’t see enough usage context on its own. More importantly, Apple keeps extending the ownership system: borrow and mutate accessors avoid unnecessary copies in computed properties, Iterable gives for loops a borrowing-based path for noncopyable or performance-sensitive types, and new standard library tools like UniqueBox, UniqueArray, safer temporary allocations, and Ref/MutableRef are basically Apple saying, “yes, you can write scary-fast code in Swift now, but maybe without immediately summoning undefined behavior.” And the larger pattern matters. Swift Build is now the default backend for Swift Package Manager, the Android workgroup shipped the first SDK as part of Swift 6.3, and Apple says Swift is now used across apps, services, firmware, and even parts of the kernel itself. That’s the real WWDC26 Swift story to me: Swift is no longer just the nice app language for Apple platforms. It’s becoming Apple’s answer to “what if one language actually scaled from UI code all the way down to systems code, while still being decent to write on a Tuesday afternoon?” #WWDC26
1
3
109
👉 mailfloss is the automated email verification service built specifically for e-commerce and D2C businesses using platforms like Klaviyo or Iterable. Read more 👉 mailfloss.com/klaviyo-vs-ite…
1
47
val result = items.fastFlatMap { it.children } So what's the difference? 🔹 flatMap() - Part of the Kotlin Standard Library - Works with any iterable - Creates iterators internally - Clean, readable, and suitable for most use cases (2/5)
1
1
311
The platform connects directly to your email service provider (including Klaviyo, Iterable, and exactly 40 ESP platforms total) and automatically scans your list every day. Read more 👉 mailfloss.com/klaviyo-vs-ite…
1
6
Creative cloud (75% of their business) is replaced by Canva, Figma, DaVinci, Nuke & Avid. Experience Cloud (approx. remaining 25%) is replaced by Braze, Iterable, Customer io, Klaviyo, Contentful, Storyblok, Amplitude, Twilio. We've been discussing Adobe for days. Please look into Canva. It's not what you think. They have direct competitors for After Effects, Premiere, and Photoshop such as Cavalry (procedural 2D animation tool they just acquired to kill AE), their own rapidly expanding Canva Video engine, and the entire Affinity suite (which completely replaces Photoshop, Illustrator, and InDesign). Don't fuck with Canva. They're coming for blood and a fight
1
68
Jun 10
My wishlist for 5.6: 1. Design I don’t need it to be perfect. I need it to be iterable. Current outputs are so bad they’re not even iterable. I regularly go to Sonnet/Haiku to get an output and get GPT to iterate. 2. Computer Use Give me a faster and more efficient Computer Use. I don’t want something that’s clicking buttons to use 100% of my limit in 1 hour. 3. De-slop shipped features. e.g. automations don’t need to have their own memory file UNLESS I WANT.
68
Jun 10
Replying to @Iterable
HEYYYY...FUCK YOU SPAMMERS
1
Dreamina Octo has officially launched in Beta! 🚀 It pioneers the concept of “VIBE CREATE” — a completely fresh creator-AI partnership built entirely from the ground up by Dreamina. Say goodbye to prompt engineering. You no longer have to struggle translating the cinematic vision in your brain into mere words. Simply chat, upload your files, and catch the vibe. Octo proactively asks clarifying questions, bridges the gaps in your creative vision, and drives your workflow forward much like an actual human collaborator. Your concepts grow organically through dialogue — easily adjustable, iterable, and constantly refining from any single frame, image, video clip, or art direction. And here is the wildest part? It replies straight back with images, video clips, and audio tracks — so instead of trying to explain the mood, the AI just shows you exactly what it means. 🔥 Designed specifically for Vibe Create workflows — it helps you transform quick sparks of inspiration, fleeting moods, and scattered thoughts into high-quality visual outputs in mere seconds.
5
14
28
21,562