SHA256
Каналы: добавить профиль через TEXT_CHANNEL_META
This commit is contained in:
@@ -36,7 +36,8 @@ public final class BodyRecordParser {
|
||||
case TextBody.KEY -> {
|
||||
if (st == (MsgSubType.TEXT_POST & 0xFFFF)
|
||||
|| st == (MsgSubType.TEXT_EDIT_POST & 0xFFFF)
|
||||
|| st == (MsgSubType.TEXT_REPOST & 0xFFFF)) {
|
||||
|| st == (MsgSubType.TEXT_REPOST & 0xFFFF)
|
||||
|| st == (MsgSubType.TEXT_CHANNEL_META & 0xFFFF)) {
|
||||
yield new TextLineBody(subType, version, bodyBytes);
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,12 @@ public final class MsgSubType {
|
||||
*/
|
||||
public static final short TEXT_REPOST = 30;
|
||||
|
||||
/**
|
||||
* CHANNEL_META — скрытый технический снимок профиля канала.
|
||||
* Имеет hasLine, использует body как POST: line-поля + UTF-8 текст.
|
||||
*/
|
||||
public static final short TEXT_CHANNEL_META = 70;
|
||||
|
||||
/* ===================== REACTION (msg_type=2) ===================== */
|
||||
|
||||
/** Лайк (LIKE). */
|
||||
|
||||
+18
-10
@@ -17,10 +17,11 @@ import java.util.Objects;
|
||||
* - POST (10)
|
||||
* - EDIT_POST (11)
|
||||
* - REPOST (30)
|
||||
* - CHANNEL_META (70)
|
||||
*
|
||||
* Формат bodyBytes (BigEndian):
|
||||
*
|
||||
* POST:
|
||||
* POST / CHANNEL_META:
|
||||
* [4] lineCode
|
||||
* [4] prevLineNumber
|
||||
* [32] prevLineHash32
|
||||
@@ -89,8 +90,9 @@ public final class TextLineBody implements BodyRecord, BodyHasLine, BodyHasTarge
|
||||
int st = this.subType & 0xFFFF;
|
||||
if (st != (MsgSubType.TEXT_POST & 0xFFFF)
|
||||
&& st != (MsgSubType.TEXT_EDIT_POST & 0xFFFF)
|
||||
&& st != (MsgSubType.TEXT_REPOST & 0xFFFF)) {
|
||||
throw new IllegalArgumentException("TextLineBody supports only POST/EDIT_POST/REPOST, got subType=" + st);
|
||||
&& st != (MsgSubType.TEXT_REPOST & 0xFFFF)
|
||||
&& st != (MsgSubType.TEXT_CHANNEL_META & 0xFFFF)) {
|
||||
throw new IllegalArgumentException("TextLineBody supports only POST/EDIT_POST/REPOST/CHANNEL_META, got subType=" + st);
|
||||
}
|
||||
|
||||
ByteBuffer bb = ByteBuffer.wrap(bodyBytes).order(ByteOrder.BIG_ENDIAN);
|
||||
@@ -133,7 +135,8 @@ public final class TextLineBody implements BodyRecord, BodyHasLine, BodyHasTarge
|
||||
this.toBlockHash32 = null;
|
||||
}
|
||||
|
||||
this.message = readStrictUtf8Len16(bb, "TextLineBody text", st == (MsgSubType.TEXT_EDIT_POST & 0xFFFF));
|
||||
this.message = readStrictUtf8Len16(bb, "TextLineBody text",
|
||||
st == (MsgSubType.TEXT_EDIT_POST & 0xFFFF) || st == (MsgSubType.TEXT_CHANNEL_META & 0xFFFF));
|
||||
|
||||
ensureNoTail(bb, "TextLineBody");
|
||||
}
|
||||
@@ -155,8 +158,9 @@ public final class TextLineBody implements BodyRecord, BodyHasLine, BodyHasTarge
|
||||
int st = subType & 0xFFFF;
|
||||
if (st != (MsgSubType.TEXT_POST & 0xFFFF)
|
||||
&& st != (MsgSubType.TEXT_EDIT_POST & 0xFFFF)
|
||||
&& st != (MsgSubType.TEXT_REPOST & 0xFFFF)) {
|
||||
throw new IllegalArgumentException("TextLineBody supports only POST/EDIT_POST/REPOST");
|
||||
&& st != (MsgSubType.TEXT_REPOST & 0xFFFF)
|
||||
&& st != (MsgSubType.TEXT_CHANNEL_META & 0xFFFF)) {
|
||||
throw new IllegalArgumentException("TextLineBody supports only POST/EDIT_POST/REPOST/CHANNEL_META");
|
||||
}
|
||||
|
||||
if (lineCode < 0) throw new IllegalArgumentException("lineCode < 0");
|
||||
@@ -206,7 +210,8 @@ public final class TextLineBody implements BodyRecord, BodyHasLine, BodyHasTarge
|
||||
int st = subType & 0xFFFF;
|
||||
if (st != (MsgSubType.TEXT_POST & 0xFFFF)
|
||||
&& st != (MsgSubType.TEXT_EDIT_POST & 0xFFFF)
|
||||
&& st != (MsgSubType.TEXT_REPOST & 0xFFFF))
|
||||
&& st != (MsgSubType.TEXT_REPOST & 0xFFFF)
|
||||
&& st != (MsgSubType.TEXT_CHANNEL_META & 0xFFFF))
|
||||
throw new IllegalArgumentException("Bad TextLineBody subType: " + st);
|
||||
|
||||
if (lineCode < 0) throw new IllegalArgumentException("lineCode < 0");
|
||||
@@ -231,10 +236,13 @@ public final class TextLineBody implements BodyRecord, BodyHasLine, BodyHasTarge
|
||||
if (toBlockHash32 == null || toBlockHash32.length != 32)
|
||||
throw new IllegalArgumentException("REPOST toBlockHash32 invalid");
|
||||
} else {
|
||||
if (message == null || message.isBlank())
|
||||
if (st == (MsgSubType.TEXT_CHANNEL_META & 0xFFFF)) {
|
||||
if (message == null) throw new IllegalArgumentException("CHANNEL_META message is null");
|
||||
} else if (message == null || message.isBlank()) {
|
||||
throw new IllegalArgumentException("Text message is blank");
|
||||
}
|
||||
if (toBlockchainName != null || toBlockGlobalNumber != null || toBlockHash32 != null)
|
||||
throw new IllegalArgumentException("POST must not contain target fields");
|
||||
throw new IllegalArgumentException("POST/CHANNEL_META must not contain target fields");
|
||||
}
|
||||
|
||||
return this;
|
||||
@@ -251,7 +259,7 @@ public final class TextLineBody implements BodyRecord, BodyHasLine, BodyHasTarge
|
||||
}
|
||||
|
||||
int cap;
|
||||
if (st == (MsgSubType.TEXT_POST & 0xFFFF)) {
|
||||
if (st == (MsgSubType.TEXT_POST & 0xFFFF) || st == (MsgSubType.TEXT_CHANNEL_META & 0xFFFF)) {
|
||||
cap = (4 + 4 + 32 + 4) + 2 + msgUtf8.length;
|
||||
} else if (st == (MsgSubType.TEXT_EDIT_POST & 0xFFFF)) {
|
||||
// EDIT_POST
|
||||
|
||||
@@ -20,9 +20,11 @@ public final class DatabaseInitializer {
|
||||
public static final int SCHEMA_VERSION_1 = 1;
|
||||
public static final int SCHEMA_VERSION_2 = 2;
|
||||
public static final int SCHEMA_VERSION_3 = 3;
|
||||
public static final int SCHEMA_VERSION_4 = 4;
|
||||
public static final String POSTGRES_SCHEMA_RESOURCE = "postgres/schema_v1.sql";
|
||||
public static final String POSTGRES_MIGRATION_V2_RESOURCE = "postgres/migration_v2.sql";
|
||||
public static final String POSTGRES_MIGRATION_V3_RESOURCE = "postgres/migration_v3.sql";
|
||||
public static final String POSTGRES_MIGRATION_V4_RESOURCE = "postgres/migration_v4.sql";
|
||||
|
||||
private DatabaseInitializer() {}
|
||||
|
||||
@@ -31,6 +33,7 @@ public final class DatabaseInitializer {
|
||||
public static final short TEXT_REPLY = 20;
|
||||
public static final short TEXT_EDIT_REPLY = 21;
|
||||
public static final short TEXT_REPOST = 30;
|
||||
public static final short TEXT_CHANNEL_META = 70;
|
||||
|
||||
public static final short REACTION_LIKE = 1;
|
||||
public static final short REACTION_UNLIKE = 2;
|
||||
@@ -88,6 +91,10 @@ public final class DatabaseInitializer {
|
||||
}
|
||||
if (currentVersion < SCHEMA_VERSION_3) {
|
||||
runSqlScript(conn, POSTGRES_MIGRATION_V3_RESOURCE);
|
||||
currentVersion = SCHEMA_VERSION_3;
|
||||
}
|
||||
if (currentVersion < SCHEMA_VERSION_4) {
|
||||
runSqlScript(conn, POSTGRES_MIGRATION_V4_RESOURCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,9 @@ public final class MsgSubType {
|
||||
/** REPOST — репост сообщения в линии канала (с комментарием и target на оригинал). */
|
||||
public static final short TEXT_REPOST = 30;
|
||||
|
||||
/** CHANNEL_META — скрытый технический снимок профиля канала. */
|
||||
public static final short TEXT_CHANNEL_META = 70;
|
||||
|
||||
/* ===================== REACTION (msg_type=2) ===================== */
|
||||
|
||||
/** Лайк (LIKE). */
|
||||
|
||||
@@ -65,8 +65,12 @@ public final class ChannelNameStateDAO {
|
||||
channel_type_version,
|
||||
channel_root_block_number,
|
||||
channel_root_block_hash,
|
||||
created_at_ms
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
created_at_ms,
|
||||
ava_ar,
|
||||
ava_sha256,
|
||||
ava_size,
|
||||
meta_updated_at_ms
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""";
|
||||
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
||||
ps.setString(1, entry.getSlug());
|
||||
@@ -79,6 +83,34 @@ public final class ChannelNameStateDAO {
|
||||
ps.setInt(8, entry.getChannelRootBlockNumber());
|
||||
ps.setBytes(9, entry.getChannelRootBlockHash());
|
||||
ps.setLong(10, entry.getCreatedAtMs());
|
||||
ps.setString(11, entry.getAvaAr() == null ? "" : entry.getAvaAr());
|
||||
ps.setString(12, entry.getAvaSha256() == null ? "" : entry.getAvaSha256());
|
||||
ps.setLong(13, Math.max(0L, entry.getAvaSize()));
|
||||
ps.setLong(14, Math.max(0L, entry.getMetaUpdatedAtMs()));
|
||||
ps.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMeta(Connection c, ChannelNameStateEntry entry) throws SQLException {
|
||||
String sql = """
|
||||
UPDATE channel_names_state
|
||||
SET display_name = ?,
|
||||
channel_description = ?,
|
||||
ava_ar = ?,
|
||||
ava_sha256 = ?,
|
||||
ava_size = ?,
|
||||
meta_updated_at_ms = ?
|
||||
WHERE owner_bch_name = ? AND channel_root_block_number = ?
|
||||
""";
|
||||
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
||||
ps.setString(1, entry.getDisplayName() == null ? "" : entry.getDisplayName());
|
||||
ps.setString(2, entry.getChannelDescription() == null ? "" : entry.getChannelDescription());
|
||||
ps.setString(3, entry.getAvaAr() == null ? "" : entry.getAvaAr());
|
||||
ps.setString(4, entry.getAvaSha256() == null ? "" : entry.getAvaSha256());
|
||||
ps.setLong(5, Math.max(0L, entry.getAvaSize()));
|
||||
ps.setLong(6, Math.max(0L, entry.getMetaUpdatedAtMs()));
|
||||
ps.setString(7, entry.getOwnerBlockchainName());
|
||||
ps.setInt(8, entry.getChannelRootBlockNumber());
|
||||
ps.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
+36
@@ -13,6 +13,10 @@ public class ChannelNameStateEntry {
|
||||
private int channelRootBlockNumber;
|
||||
private byte[] channelRootBlockHash;
|
||||
private long createdAtMs;
|
||||
private String avaAr;
|
||||
private String avaSha256;
|
||||
private long avaSize;
|
||||
private long metaUpdatedAtMs;
|
||||
|
||||
public String getSlug() {
|
||||
return slug;
|
||||
@@ -93,4 +97,36 @@ public class ChannelNameStateEntry {
|
||||
public void setCreatedAtMs(long createdAtMs) {
|
||||
this.createdAtMs = createdAtMs;
|
||||
}
|
||||
|
||||
public String getAvaAr() {
|
||||
return avaAr;
|
||||
}
|
||||
|
||||
public void setAvaAr(String avaAr) {
|
||||
this.avaAr = avaAr;
|
||||
}
|
||||
|
||||
public String getAvaSha256() {
|
||||
return avaSha256;
|
||||
}
|
||||
|
||||
public void setAvaSha256(String avaSha256) {
|
||||
this.avaSha256 = avaSha256;
|
||||
}
|
||||
|
||||
public long getAvaSize() {
|
||||
return avaSize;
|
||||
}
|
||||
|
||||
public void setAvaSize(long avaSize) {
|
||||
this.avaSize = avaSize;
|
||||
}
|
||||
|
||||
public long getMetaUpdatedAtMs() {
|
||||
return metaUpdatedAtMs;
|
||||
}
|
||||
|
||||
public void setMetaUpdatedAtMs(long metaUpdatedAtMs) {
|
||||
this.metaUpdatedAtMs = metaUpdatedAtMs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE channel_names_state
|
||||
ADD COLUMN IF NOT EXISTS ava_ar TEXT NOT NULL DEFAULT '',
|
||||
ADD COLUMN IF NOT EXISTS ava_sha256 TEXT NOT NULL DEFAULT '',
|
||||
ADD COLUMN IF NOT EXISTS ava_size BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS meta_updated_at_ms BIGINT NOT NULL DEFAULT 0;
|
||||
|
||||
INSERT INTO db_schema_version (id, schema_version, updated_at_ms)
|
||||
VALUES (1, 4, CAST(EXTRACT(EPOCH FROM clock_timestamp()) * 1000 AS BIGINT))
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
schema_version = EXCLUDED.schema_version,
|
||||
updated_at_ms = EXCLUDED.updated_at_ms;
|
||||
|
||||
COMMIT;
|
||||
@@ -536,7 +536,11 @@ CREATE TABLE IF NOT EXISTS channel_names_state (
|
||||
channel_type_version INTEGER NOT NULL DEFAULT 1,
|
||||
channel_root_block_number INTEGER NOT NULL,
|
||||
channel_root_block_hash BYTEA NOT NULL,
|
||||
created_at_ms BIGINT NOT NULL
|
||||
created_at_ms BIGINT NOT NULL,
|
||||
ava_ar TEXT NOT NULL DEFAULT '',
|
||||
ava_sha256 TEXT NOT NULL DEFAULT '',
|
||||
ava_size BIGINT NOT NULL DEFAULT 0,
|
||||
meta_updated_at_ms BIGINT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_channel_names_state_owner_type_slug
|
||||
|
||||
+59
-1
@@ -21,6 +21,7 @@ import server.sync.BlockchainResyncGuard;
|
||||
import server.logic.ws_protocol.JSON.handlers.blockchain.Net_AddBlock_Handler_utils.BlockchainWriter;
|
||||
import server.logic.ws_protocol.JSON.handlers.blockchain.entyties.Net_AddBlock_Request;
|
||||
import server.logic.ws_protocol.JSON.handlers.blockchain.entyties.Net_AddBlock_Response;
|
||||
import server.logic.ws_protocol.JSON.handlers.channels.ChannelMetaTextParser;
|
||||
import server.logic.ws_protocol.JSON.handlers.channels.ChannelNamesStateBootstrapper;
|
||||
import server.logic.ws_protocol.WireCodes;
|
||||
import server.sync.AddBlockSyncService;
|
||||
@@ -277,6 +278,7 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
}
|
||||
|
||||
ChannelNameStateEntry channelNameStateEntry = null;
|
||||
ChannelNameStateEntry channelMetaUpdateEntry = null;
|
||||
Chat200CreateSeed chat200CreateSeed = null;
|
||||
if (block.body instanceof CreateChannelBody createChannelBody) {
|
||||
int channelTypeCode = Short.toUnsignedInt(createChannelBody.channelTypeCode);
|
||||
@@ -329,6 +331,39 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
chat200CreateSeed.chatTitle = channelNameStateEntry.getChannelDescription();
|
||||
chat200CreateSeed.updatedAtMs = block.timestamp * 1000L;
|
||||
}
|
||||
} else if ((block.type & 0xFFFF) == 1
|
||||
&& (block.subType & 0xFFFF) == (MsgSubType.TEXT_CHANNEL_META & 0xFFFF)
|
||||
&& block.body instanceof TextLineBody metaBody) {
|
||||
int lineCode = metaBody.lineCode();
|
||||
if (lineCode <= 0) {
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_channel_meta_line", serverLastNum, serverLastHashHex);
|
||||
}
|
||||
ExistingChannelState existingChannel;
|
||||
try {
|
||||
existingChannel = loadExistingChannelState(blockchainName, lineCode);
|
||||
} catch (Exception e) {
|
||||
log.error("AddBlock: channel_meta_lookup_failed (login={}, blockchainName={}, lineCode={})",
|
||||
login, blockchainName, lineCode, e);
|
||||
return new AddBlockResult(WireCodes.Status.INTERNAL_ERROR, "internal_error", serverLastNum, serverLastHashHex);
|
||||
}
|
||||
if (existingChannel == null) {
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "channel_not_found", serverLastNum, serverLastHashHex);
|
||||
}
|
||||
ChannelMetaTextParser.Parsed parsedMeta;
|
||||
try {
|
||||
parsedMeta = ChannelMetaTextParser.parseStrict(metaBody.message, existingChannel.slug);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, e.getMessage(), serverLastNum, serverLastHashHex);
|
||||
}
|
||||
channelMetaUpdateEntry = new ChannelNameStateEntry();
|
||||
channelMetaUpdateEntry.setOwnerBlockchainName(blockchainName);
|
||||
channelMetaUpdateEntry.setChannelRootBlockNumber(lineCode);
|
||||
channelMetaUpdateEntry.setDisplayName(parsedMeta.title());
|
||||
channelMetaUpdateEntry.setChannelDescription(parsedMeta.description());
|
||||
channelMetaUpdateEntry.setAvaAr(parsedMeta.avaAr());
|
||||
channelMetaUpdateEntry.setAvaSha256(parsedMeta.avaSha256());
|
||||
channelMetaUpdateEntry.setAvaSize(parsedMeta.avaSize());
|
||||
channelMetaUpdateEntry.setMetaUpdatedAtMs(block.timestamp * 1000L);
|
||||
}
|
||||
|
||||
// 4.2) запрет дырок: blockNumber строго last+1
|
||||
@@ -485,7 +520,7 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
);
|
||||
}
|
||||
|
||||
dbWriter.appendBlockAndState(blockchainName, block, st, be, upsertedParam, channelNameStateEntry);
|
||||
dbWriter.appendBlockAndState(blockchainName, block, st, be, upsertedParam, channelNameStateEntry, channelMetaUpdateEntry);
|
||||
|
||||
if (chat200CreateSeed != null) {
|
||||
upsertChat200StateFromCreate(chat200CreateSeed);
|
||||
@@ -544,6 +579,29 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
long updatedAtMs;
|
||||
}
|
||||
|
||||
private static final class ExistingChannelState {
|
||||
String slug;
|
||||
}
|
||||
|
||||
private ExistingChannelState loadExistingChannelState(String ownerBch, int rootBlockNumber) throws Exception {
|
||||
try (Connection c = shine.db.DbController.getInstance().getConnection();
|
||||
PreparedStatement ps = c.prepareStatement("""
|
||||
SELECT slug
|
||||
FROM channel_names_state
|
||||
WHERE owner_bch_name = ? AND channel_root_block_number = ?
|
||||
LIMIT 1
|
||||
""")) {
|
||||
ps.setString(1, ownerBch);
|
||||
ps.setInt(2, rootBlockNumber);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (!rs.next()) return null;
|
||||
ExistingChannelState state = new ExistingChannelState();
|
||||
state.slug = rs.getString("slug");
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void upsertChat200StateFromCreate(Chat200CreateSeed seed) throws Exception {
|
||||
try (Connection c = shine.db.DbController.getInstance().getConnection();
|
||||
PreparedStatement ps = c.prepareStatement("""
|
||||
|
||||
+6
-1
@@ -58,7 +58,8 @@ public final class BlockchainWriter {
|
||||
BlockchainStateEntry st,
|
||||
BlockEntry be,
|
||||
UserParamEntry userParamEntry,
|
||||
ChannelNameStateEntry channelNameStateEntry) throws SQLException {
|
||||
ChannelNameStateEntry channelNameStateEntry,
|
||||
ChannelNameStateEntry channelMetaUpdateEntry) throws SQLException {
|
||||
|
||||
long nowMs = System.currentTimeMillis();
|
||||
byte[] blockBytes = block.toBytes();
|
||||
@@ -91,6 +92,10 @@ public final class BlockchainWriter {
|
||||
channelNameStateDAO.insert(c, channelNameStateEntry);
|
||||
}
|
||||
|
||||
if (channelMetaUpdateEntry != null) {
|
||||
channelNameStateDAO.updateMeta(c, channelMetaUpdateEntry);
|
||||
}
|
||||
|
||||
c.commit();
|
||||
committed = true;
|
||||
} catch (Exception e) {
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package server.logic.ws_protocol.JSON.handlers.channels;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class ChannelMetaTextParser {
|
||||
public static final int MAX_TITLE_CHARS = 50;
|
||||
public static final int MAX_DESCRIPTION_CHARS = 250;
|
||||
|
||||
private ChannelMetaTextParser() {}
|
||||
|
||||
public static Parsed parseStrict(String rawText, String fallbackTitle) {
|
||||
String text = String.valueOf(rawText == null ? "" : rawText);
|
||||
String title = "";
|
||||
String avaAr = "";
|
||||
String avaSha256 = "";
|
||||
long avaSize = 0L;
|
||||
boolean seenTitle = false;
|
||||
boolean seenAvatar = false;
|
||||
|
||||
int offset = 0;
|
||||
while (text.startsWith("<SHiNE:", offset)) {
|
||||
int end = text.indexOf('>', offset);
|
||||
if (end < 0) throw new IllegalArgumentException("bad_channel_meta_tag");
|
||||
String body = text.substring(offset + "<SHiNE:".length(), end);
|
||||
if (body.startsWith("title;")) {
|
||||
if (seenTitle) throw new IllegalArgumentException("duplicate_channel_meta_title");
|
||||
seenTitle = true;
|
||||
title = parseTitleTag(body);
|
||||
} else if (body.startsWith("avatar;")) {
|
||||
if (seenAvatar) throw new IllegalArgumentException("duplicate_channel_meta_avatar");
|
||||
seenAvatar = true;
|
||||
Avatar avatar = parseAvatarTag(body);
|
||||
avaAr = avatar.ar;
|
||||
avaSha256 = avatar.sha256;
|
||||
avaSize = avatar.size;
|
||||
} else {
|
||||
throw new IllegalArgumentException("unknown_channel_meta_tag");
|
||||
}
|
||||
offset = end + 1;
|
||||
if (offset < text.length() && text.charAt(offset) == '\n') offset += 1;
|
||||
}
|
||||
|
||||
String description = text.substring(offset).trim();
|
||||
if (countCodePoints(description) > MAX_DESCRIPTION_CHARS) {
|
||||
throw new IllegalArgumentException("channel_meta_description_too_long");
|
||||
}
|
||||
|
||||
String cleanTitle = title.isBlank() ? String.valueOf(fallbackTitle == null ? "" : fallbackTitle).trim() : title.trim();
|
||||
validateTitle(cleanTitle);
|
||||
|
||||
return new Parsed(cleanTitle, description, avaAr, avaSha256, avaSize);
|
||||
}
|
||||
|
||||
private static String parseTitleTag(String body) {
|
||||
String prefix = "title;v=1;";
|
||||
if (!body.startsWith(prefix)) throw new IllegalArgumentException("bad_channel_meta_title");
|
||||
String title = body.substring(prefix.length()).trim();
|
||||
validateTitle(title);
|
||||
return title;
|
||||
}
|
||||
|
||||
private static Avatar parseAvatarTag(String body) {
|
||||
Map<String, String> fields = parseFields(body.substring("avatar;".length()));
|
||||
if (!"1".equals(fields.get("v"))) throw new IllegalArgumentException("bad_channel_meta_avatar_version");
|
||||
String ar = String.valueOf(fields.getOrDefault("ar", "")).trim();
|
||||
String sha256 = String.valueOf(fields.getOrDefault("sha256", "")).trim().toLowerCase();
|
||||
String sizeRaw = String.valueOf(fields.getOrDefault("size", "")).trim();
|
||||
if (!ar.matches("^[A-Za-z0-9_-]{43}$")) throw new IllegalArgumentException("bad_channel_meta_avatar_ar");
|
||||
if (!sha256.matches("^[0-9a-f]{64}$")) throw new IllegalArgumentException("bad_channel_meta_avatar_sha256");
|
||||
long size;
|
||||
try {
|
||||
size = Long.parseLong(sizeRaw);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("bad_channel_meta_avatar_size");
|
||||
}
|
||||
if (size <= 0L) throw new IllegalArgumentException("bad_channel_meta_avatar_size");
|
||||
return new Avatar(ar, sha256, size);
|
||||
}
|
||||
|
||||
private static Map<String, String> parseFields(String raw) {
|
||||
Map<String, String> out = new HashMap<>();
|
||||
for (String part : String.valueOf(raw == null ? "" : raw).split(";")) {
|
||||
int eq = part.indexOf('=');
|
||||
if (eq <= 0) continue;
|
||||
String key = part.substring(0, eq).trim();
|
||||
String value = part.substring(eq + 1).trim();
|
||||
if (!key.isEmpty()) out.put(key, value);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static void validateTitle(String title) {
|
||||
String value = String.valueOf(title == null ? "" : title);
|
||||
if (value.indexOf('<') >= 0 || value.indexOf('>') >= 0 || value.indexOf(';') >= 0
|
||||
|| value.indexOf('\t') >= 0 || value.indexOf('\n') >= 0 || value.indexOf('\r') >= 0
|
||||
|| value.indexOf('\0') >= 0) {
|
||||
throw new IllegalArgumentException("bad_channel_meta_title_chars");
|
||||
}
|
||||
if (countCodePoints(value) > MAX_TITLE_CHARS) {
|
||||
throw new IllegalArgumentException("channel_meta_title_too_long");
|
||||
}
|
||||
}
|
||||
|
||||
private static int countCodePoints(String value) {
|
||||
return String.valueOf(value == null ? "" : value).codePointCount(0, String.valueOf(value == null ? "" : value).length());
|
||||
}
|
||||
|
||||
private record Avatar(String ar, String sha256, long size) {}
|
||||
|
||||
public record Parsed(String title, String description, String avaAr, String avaSha256, long avaSize) {}
|
||||
}
|
||||
+127
-50
@@ -23,8 +23,6 @@ final class ChannelsReadSupport {
|
||||
static final int MSG_TYPE_REACTION = 2;
|
||||
static final int MSG_TYPE_TECH = 0;
|
||||
static final String STORIES_CHANNEL_NAME = "stories";
|
||||
static final String COMMAND_PREFIX = "/.";
|
||||
static final String COMMAND_DESC = "desc";
|
||||
static final String COMMAND_ADD = "add";
|
||||
static final String COMMAND_REMOVE = "remove";
|
||||
|
||||
@@ -47,11 +45,40 @@ final class ChannelsReadSupport {
|
||||
|
||||
if (rootNumber == 0) {
|
||||
meta.channelName = STORIES_CHANNEL_NAME;
|
||||
meta.channelDescription = detectLatestDescriptionCommand(c, ownerBch, 0);
|
||||
meta.displayName = STORIES_CHANNEL_NAME;
|
||||
meta.channelDescription = "";
|
||||
meta.channelTypeCode = CreateChannelBody.CHANNEL_TYPE_STORIES & 0xFFFF;
|
||||
return meta;
|
||||
}
|
||||
|
||||
String stateSql = """
|
||||
SELECT slug, display_name, channel_description, channel_type_code, channel_type_version,
|
||||
ava_ar, ava_sha256, ava_size, meta_updated_at_ms
|
||||
FROM channel_names_state
|
||||
WHERE owner_bch_name = ? AND channel_root_block_number = ?
|
||||
LIMIT 1
|
||||
""";
|
||||
try (PreparedStatement ps = c.prepareStatement(stateSql)) {
|
||||
ps.setString(1, ownerBch);
|
||||
ps.setInt(2, rootNumber);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
meta.channelName = rs.getString("slug");
|
||||
meta.displayName = rs.getString("display_name");
|
||||
meta.channelDescription = String.valueOf(rs.getString("channel_description") == null ? "" : rs.getString("channel_description"));
|
||||
meta.channelTypeCode = rs.getInt("channel_type_code");
|
||||
meta.channelTypeVersion = rs.getInt("channel_type_version");
|
||||
meta.avaAr = String.valueOf(rs.getString("ava_ar") == null ? "" : rs.getString("ava_ar"));
|
||||
meta.avaSha256 = String.valueOf(rs.getString("ava_sha256") == null ? "" : rs.getString("ava_sha256"));
|
||||
meta.avaSize = rs.getLong("ava_size");
|
||||
meta.metaUpdatedAtMs = rs.getLong("meta_updated_at_ms");
|
||||
return meta;
|
||||
}
|
||||
}
|
||||
} catch (SQLException ignored) {
|
||||
// fallback ниже для окружений до миграции.
|
||||
}
|
||||
|
||||
String sql = "SELECT block_bytes FROM blocks WHERE bch_name=? AND block_number=? LIMIT 1";
|
||||
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
||||
ps.setString(1, ownerBch);
|
||||
@@ -68,21 +95,20 @@ final class ChannelsReadSupport {
|
||||
BodyRecord body = e.body;
|
||||
if (body instanceof CreateChannelBody ccb) {
|
||||
meta.channelName = ccb.channelName;
|
||||
meta.displayName = ccb.channelName;
|
||||
meta.channelDescription = ccb.channelDescription == null ? "" : ccb.channelDescription;
|
||||
meta.channelTypeCode = Short.toUnsignedInt(ccb.channelTypeCode);
|
||||
meta.channelTypeVersion = Short.toUnsignedInt(ccb.channelTypeVersion);
|
||||
} else {
|
||||
meta.channelName = null;
|
||||
meta.displayName = null;
|
||||
meta.channelDescription = "";
|
||||
meta.channelTypeCode = CreateChannelBody.CHANNEL_TYPE_PUBLIC & 0xFFFF;
|
||||
}
|
||||
String updatedDescription = detectLatestDescriptionCommand(c, ownerBch, rootNumber);
|
||||
if (updatedDescription != null) {
|
||||
meta.channelDescription = updatedDescription;
|
||||
}
|
||||
return meta;
|
||||
} catch (Exception ignored) {
|
||||
meta.channelName = null;
|
||||
meta.displayName = null;
|
||||
meta.channelDescription = "";
|
||||
meta.channelTypeCode = CreateChannelBody.CHANNEL_TYPE_PUBLIC & 0xFFFF;
|
||||
return meta;
|
||||
@@ -106,7 +132,7 @@ final class ChannelsReadSupport {
|
||||
|
||||
static PostBlock loadLastPost(Connection c, String ownerBch, int lineCode) throws SQLException {
|
||||
String sql = """
|
||||
SELECT login,bch_name,block_number,block_hash,block_bytes
|
||||
SELECT login,bch_name,block_number,block_hash,block_bytes,this_line_number
|
||||
FROM blocks
|
||||
WHERE bch_name=? AND msg_type=? AND msg_sub_type IN (?, ?) AND line_code=?
|
||||
ORDER BY block_number DESC
|
||||
@@ -270,8 +296,7 @@ final class ChannelsReadSupport {
|
||||
|
||||
static String detectChannelDescription(Connection c, String ownerBch, int rootNumber) throws SQLException {
|
||||
if (rootNumber == 0) {
|
||||
String fromCommand = detectLatestDescriptionCommand(c, ownerBch, 0);
|
||||
return fromCommand == null ? "" : fromCommand;
|
||||
return "";
|
||||
}
|
||||
|
||||
// Preferred source: persisted state (fast path, works for CreateChannelBody v2).
|
||||
@@ -287,8 +312,7 @@ final class ChannelsReadSupport {
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
String saved = String.valueOf(rs.getString("channel_description") == null ? "" : rs.getString("channel_description"));
|
||||
String fromCommand = detectLatestDescriptionCommand(c, ownerBch, rootNumber);
|
||||
return fromCommand == null ? saved : fromCommand;
|
||||
return saved;
|
||||
}
|
||||
}
|
||||
} catch (SQLException ignored) {
|
||||
@@ -306,8 +330,6 @@ final class ChannelsReadSupport {
|
||||
BchBlockEntry e = new BchBlockEntry(bytes);
|
||||
BodyRecord body = e.body;
|
||||
if (body instanceof CreateChannelBody ccb) {
|
||||
String fromCommand = detectLatestDescriptionCommand(c, ownerBch, rootNumber);
|
||||
if (fromCommand != null) return fromCommand;
|
||||
return ccb.channelDescription == null ? "" : ccb.channelDescription;
|
||||
}
|
||||
return "";
|
||||
@@ -363,45 +385,90 @@ final class ChannelsReadSupport {
|
||||
return meta.channelTypeVersion;
|
||||
}
|
||||
|
||||
static String detectLatestDescriptionCommand(Connection c, String ownerBch, int lineCode) throws SQLException {
|
||||
String sql = """
|
||||
SELECT block_bytes
|
||||
static List<MetaEvent> channelMetaEvents(Connection c, String ownerBch, int lineCode) throws SQLException {
|
||||
List<MetaEvent> out = new ArrayList<>();
|
||||
if (lineCode <= 0) return out;
|
||||
ChannelMeta base = detectChannelMeta(c, ownerBch, lineCode);
|
||||
String rootSql = """
|
||||
SELECT login,bch_name,block_number,block_hash,block_bytes
|
||||
FROM blocks
|
||||
WHERE bch_name=? AND msg_type=? AND msg_sub_type IN (?, ?) AND line_code=?
|
||||
ORDER BY block_number DESC
|
||||
LIMIT 300
|
||||
WHERE bch_name=? AND msg_type=? AND msg_sub_type=? AND block_number=?
|
||||
LIMIT 1
|
||||
""";
|
||||
try (PreparedStatement ps = c.prepareStatement(rootSql)) {
|
||||
ps.setString(1, ownerBch);
|
||||
ps.setInt(2, MSG_TYPE_TECH);
|
||||
ps.setInt(3, 1);
|
||||
ps.setInt(4, lineCode);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
MetaEvent event = new MetaEvent();
|
||||
event.kind = "created";
|
||||
event.login = rs.getString("login");
|
||||
event.bchName = rs.getString("bch_name");
|
||||
event.blockNumber = rs.getInt("block_number");
|
||||
event.blockHash = rs.getBytes("block_hash");
|
||||
event.lineStep = (Integer) rs.getObject("this_line_number");
|
||||
try {
|
||||
BchBlockEntry entry = new BchBlockEntry(rs.getBytes("block_bytes"));
|
||||
event.createdAtMs = entry.timestamp * 1000L;
|
||||
if (entry.body instanceof CreateChannelBody ccb) {
|
||||
event.title = ccb.channelName;
|
||||
event.description = ccb.channelDescription == null ? "" : ccb.channelDescription;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
event.createdAtMs = 0L;
|
||||
}
|
||||
if (event.title == null || event.title.isBlank()) {
|
||||
event.title = base.displayName == null || base.displayName.isBlank() ? base.channelName : base.displayName;
|
||||
}
|
||||
if (event.description == null) event.description = "";
|
||||
event.avaAr = "";
|
||||
event.avaSha256 = "";
|
||||
event.avaSize = 0L;
|
||||
out.add(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String sql = """
|
||||
SELECT login,bch_name,block_number,block_hash,block_bytes,this_line_number
|
||||
FROM blocks
|
||||
WHERE bch_name=? AND msg_type=? AND msg_sub_type=? AND line_code=?
|
||||
ORDER BY block_number ASC
|
||||
""";
|
||||
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
||||
ps.setString(1, ownerBch);
|
||||
ps.setInt(2, MSG_TYPE_TEXT);
|
||||
ps.setInt(3, MsgSubType.TEXT_POST);
|
||||
ps.setInt(4, MsgSubType.TEXT_REPOST);
|
||||
ps.setInt(5, lineCode);
|
||||
ps.setInt(3, MsgSubType.TEXT_CHANNEL_META);
|
||||
ps.setInt(4, lineCode);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
TextInfo info = parseTextAndTime(rs.getBytes("block_bytes"));
|
||||
CommandInfo commandInfo = parseCommandText(info.text);
|
||||
if (commandInfo != null && COMMAND_DESC.equals(commandInfo.command)) {
|
||||
return commandInfo.arg;
|
||||
byte[] bytes = rs.getBytes("block_bytes");
|
||||
TextInfo info = parseTextAndTime(bytes);
|
||||
try {
|
||||
ChannelMetaTextParser.Parsed parsed = ChannelMetaTextParser.parseStrict(info.text, base.channelName);
|
||||
MetaEvent event = new MetaEvent();
|
||||
event.kind = "profile_updated";
|
||||
event.login = rs.getString("login");
|
||||
event.bchName = rs.getString("bch_name");
|
||||
event.blockNumber = rs.getInt("block_number");
|
||||
event.blockHash = rs.getBytes("block_hash");
|
||||
event.lineStep = (Integer) rs.getObject("this_line_number");
|
||||
event.createdAtMs = info.createdAtMs;
|
||||
event.title = parsed.title();
|
||||
event.description = parsed.description();
|
||||
event.avaAr = parsed.avaAr();
|
||||
event.avaSha256 = parsed.avaSha256();
|
||||
event.avaSize = parsed.avaSize();
|
||||
out.add(event);
|
||||
} catch (Exception ignored) {
|
||||
// Невалидный meta-блок не участвует в системной истории UI.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static CommandInfo parseCommandText(String text) {
|
||||
String value = String.valueOf(text == null ? "" : text).trim();
|
||||
if (!value.startsWith(COMMAND_PREFIX)) return null;
|
||||
|
||||
String raw = value.substring(COMMAND_PREFIX.length()).trim();
|
||||
if (raw.isEmpty()) return null;
|
||||
|
||||
int sp = raw.indexOf(' ');
|
||||
String cmd = (sp < 0 ? raw : raw.substring(0, sp)).trim().toLowerCase();
|
||||
String arg = sp < 0 ? "" : raw.substring(sp + 1).trim();
|
||||
if (cmd.isEmpty()) return null;
|
||||
return new CommandInfo(cmd, arg);
|
||||
return out;
|
||||
}
|
||||
|
||||
static PairChannelSelector findPersonalPairChannel(Connection c, String ownerLogin, String partnerLogin) throws SQLException {
|
||||
@@ -526,19 +593,29 @@ final class ChannelsReadSupport {
|
||||
|
||||
static final class ChannelMeta {
|
||||
String channelName;
|
||||
String displayName;
|
||||
String channelDescription;
|
||||
int channelTypeCode;
|
||||
int channelTypeVersion;
|
||||
String avaAr = "";
|
||||
String avaSha256 = "";
|
||||
long avaSize = 0L;
|
||||
long metaUpdatedAtMs = 0L;
|
||||
}
|
||||
|
||||
static final class CommandInfo {
|
||||
final String command;
|
||||
final String arg;
|
||||
|
||||
CommandInfo(String command, String arg) {
|
||||
this.command = command;
|
||||
this.arg = arg;
|
||||
}
|
||||
static final class MetaEvent {
|
||||
String kind;
|
||||
String login;
|
||||
String bchName;
|
||||
int blockNumber;
|
||||
byte[] blockHash;
|
||||
Integer lineStep;
|
||||
long createdAtMs;
|
||||
String title;
|
||||
String description;
|
||||
String avaAr;
|
||||
String avaSha256;
|
||||
long avaSize;
|
||||
}
|
||||
|
||||
static final class PairChannelSelector {
|
||||
|
||||
+26
@@ -57,7 +57,12 @@ public class Net_GetChannelMessages_Handler implements JsonMessageHandler {
|
||||
channel.setOwnerLogin(BlockchainNameUtil.loginFromBlockchainName(ownerBch));
|
||||
ChannelsReadSupport.ChannelMeta meta = ChannelsReadSupport.detectChannelMeta(c, ownerBch, lineCode);
|
||||
channel.setChannelName(meta.channelName);
|
||||
channel.setDisplayName(meta.displayName == null || meta.displayName.isBlank() ? meta.channelName : meta.displayName);
|
||||
channel.setChannelDescription(meta.channelDescription);
|
||||
channel.setAvaAr(meta.avaAr);
|
||||
channel.setAvaSha256(meta.avaSha256);
|
||||
channel.setAvaSize(meta.avaSize);
|
||||
channel.setMetaUpdatedAtMs(meta.metaUpdatedAtMs);
|
||||
channel.setChannelTypeCode(meta.channelTypeCode);
|
||||
channel.setChannelTypeVersion(meta.channelTypeVersion);
|
||||
Net_GetChannelMessages_Response.BlockRef rootRef = new Net_GetChannelMessages_Response.BlockRef();
|
||||
@@ -66,6 +71,27 @@ public class Net_GetChannelMessages_Handler implements JsonMessageHandler {
|
||||
channel.setChannelRoot(rootRef);
|
||||
resp.setChannel(channel);
|
||||
|
||||
List<Net_GetChannelMessages_Response.ChannelMetaEvent> metaEvents = new ArrayList<>();
|
||||
for (ChannelsReadSupport.MetaEvent event : ChannelsReadSupport.channelMetaEvents(c, ownerBch, lineCode)) {
|
||||
Net_GetChannelMessages_Response.ChannelMetaEvent outEvent = new Net_GetChannelMessages_Response.ChannelMetaEvent();
|
||||
outEvent.setKind(event.kind);
|
||||
Net_GetChannelMessages_Response.BlockRef eventRef = new Net_GetChannelMessages_Response.BlockRef();
|
||||
eventRef.setBlockNumber(event.blockNumber);
|
||||
eventRef.setBlockHash(ChannelsReadSupport.toHex(event.blockHash));
|
||||
outEvent.setMessageRef(eventRef);
|
||||
outEvent.setAuthorLogin(event.login);
|
||||
outEvent.setAuthorBlockchainName(event.bchName);
|
||||
outEvent.setLineStep(event.lineStep);
|
||||
outEvent.setCreatedAtMs(event.createdAtMs);
|
||||
outEvent.setTitle(event.title);
|
||||
outEvent.setDescription(event.description);
|
||||
outEvent.setAvaAr(event.avaAr);
|
||||
outEvent.setAvaSha256(event.avaSha256);
|
||||
outEvent.setAvaSize(event.avaSize);
|
||||
metaEvents.add(outEvent);
|
||||
}
|
||||
resp.setMetaEvents(metaEvents);
|
||||
|
||||
List<ChannelsReadSupport.PostBlock> posts = new ArrayList<>(
|
||||
ChannelsReadSupport.channelPosts(c, ownerBch, lineCode, limit, asc)
|
||||
);
|
||||
|
||||
+5
@@ -65,7 +65,12 @@ public class Net_ListSubscriptionsFeed_Handler implements JsonMessageHandler {
|
||||
channelRef.setOwnerLogin(key.ownerLogin);
|
||||
channelRef.setOwnerBlockchainName(key.ownerBch);
|
||||
channelRef.setChannelName(meta.channelName);
|
||||
channelRef.setDisplayName(meta.displayName == null || meta.displayName.isBlank() ? meta.channelName : meta.displayName);
|
||||
channelRef.setChannelDescription(meta.channelDescription);
|
||||
channelRef.setAvaAr(meta.avaAr);
|
||||
channelRef.setAvaSha256(meta.avaSha256);
|
||||
channelRef.setAvaSize(meta.avaSize);
|
||||
channelRef.setMetaUpdatedAtMs(meta.metaUpdatedAtMs);
|
||||
channelRef.setChannelTypeCode(meta.channelTypeCode);
|
||||
channelRef.setChannelTypeVersion(meta.channelTypeVersion);
|
||||
channelRef.setPersonal(meta.channelTypeCode == 100);
|
||||
|
||||
+71
@@ -8,6 +8,7 @@ import java.util.List;
|
||||
public class Net_GetChannelMessages_Response extends Net_Response {
|
||||
private Channel channel;
|
||||
private List<MessageItem> messages = new ArrayList<>();
|
||||
private List<ChannelMetaEvent> metaEvents = new ArrayList<>();
|
||||
|
||||
public Channel getChannel() { return channel; }
|
||||
public void setChannel(Channel channel) { this.channel = channel; }
|
||||
@@ -15,12 +16,20 @@ public class Net_GetChannelMessages_Response extends Net_Response {
|
||||
public List<MessageItem> getMessages() { return messages; }
|
||||
public void setMessages(List<MessageItem> messages) { this.messages = messages; }
|
||||
|
||||
public List<ChannelMetaEvent> getMetaEvents() { return metaEvents; }
|
||||
public void setMetaEvents(List<ChannelMetaEvent> metaEvents) { this.metaEvents = metaEvents; }
|
||||
|
||||
|
||||
public static class Channel {
|
||||
private String ownerLogin;
|
||||
private String ownerBlockchainName;
|
||||
private String channelName;
|
||||
private String displayName;
|
||||
private String channelDescription;
|
||||
private String avaAr;
|
||||
private String avaSha256;
|
||||
private Long avaSize;
|
||||
private Long metaUpdatedAtMs;
|
||||
private Integer channelTypeCode;
|
||||
private Integer channelTypeVersion;
|
||||
private BlockRef channelRoot;
|
||||
@@ -34,9 +43,24 @@ public class Net_GetChannelMessages_Response extends Net_Response {
|
||||
public String getChannelName() { return channelName; }
|
||||
public void setChannelName(String channelName) { this.channelName = channelName; }
|
||||
|
||||
public String getDisplayName() { return displayName; }
|
||||
public void setDisplayName(String displayName) { this.displayName = displayName; }
|
||||
|
||||
public String getChannelDescription() { return channelDescription; }
|
||||
public void setChannelDescription(String channelDescription) { this.channelDescription = channelDescription; }
|
||||
|
||||
public String getAvaAr() { return avaAr; }
|
||||
public void setAvaAr(String avaAr) { this.avaAr = avaAr; }
|
||||
|
||||
public String getAvaSha256() { return avaSha256; }
|
||||
public void setAvaSha256(String avaSha256) { this.avaSha256 = avaSha256; }
|
||||
|
||||
public Long getAvaSize() { return avaSize; }
|
||||
public void setAvaSize(Long avaSize) { this.avaSize = avaSize; }
|
||||
|
||||
public Long getMetaUpdatedAtMs() { return metaUpdatedAtMs; }
|
||||
public void setMetaUpdatedAtMs(Long metaUpdatedAtMs) { this.metaUpdatedAtMs = metaUpdatedAtMs; }
|
||||
|
||||
public Integer getChannelTypeCode() { return channelTypeCode; }
|
||||
public void setChannelTypeCode(Integer channelTypeCode) { this.channelTypeCode = channelTypeCode; }
|
||||
|
||||
@@ -47,6 +71,53 @@ public class Net_GetChannelMessages_Response extends Net_Response {
|
||||
public void setChannelRoot(BlockRef channelRoot) { this.channelRoot = channelRoot; }
|
||||
}
|
||||
|
||||
public static class ChannelMetaEvent {
|
||||
private String kind;
|
||||
private BlockRef messageRef;
|
||||
private String authorLogin;
|
||||
private String authorBlockchainName;
|
||||
private Integer lineStep;
|
||||
private long createdAtMs;
|
||||
private String title;
|
||||
private String description;
|
||||
private String avaAr;
|
||||
private String avaSha256;
|
||||
private Long avaSize;
|
||||
|
||||
public String getKind() { return kind; }
|
||||
public void setKind(String kind) { this.kind = kind; }
|
||||
|
||||
public BlockRef getMessageRef() { return messageRef; }
|
||||
public void setMessageRef(BlockRef messageRef) { this.messageRef = messageRef; }
|
||||
|
||||
public String getAuthorLogin() { return authorLogin; }
|
||||
public void setAuthorLogin(String authorLogin) { this.authorLogin = authorLogin; }
|
||||
|
||||
public String getAuthorBlockchainName() { return authorBlockchainName; }
|
||||
public void setAuthorBlockchainName(String authorBlockchainName) { this.authorBlockchainName = authorBlockchainName; }
|
||||
|
||||
public Integer getLineStep() { return lineStep; }
|
||||
public void setLineStep(Integer lineStep) { this.lineStep = lineStep; }
|
||||
|
||||
public long getCreatedAtMs() { return createdAtMs; }
|
||||
public void setCreatedAtMs(long createdAtMs) { this.createdAtMs = createdAtMs; }
|
||||
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
|
||||
public String getAvaAr() { return avaAr; }
|
||||
public void setAvaAr(String avaAr) { this.avaAr = avaAr; }
|
||||
|
||||
public String getAvaSha256() { return avaSha256; }
|
||||
public void setAvaSha256(String avaSha256) { this.avaSha256 = avaSha256; }
|
||||
|
||||
public Long getAvaSize() { return avaSize; }
|
||||
public void setAvaSize(Long avaSize) { this.avaSize = avaSize; }
|
||||
}
|
||||
|
||||
public static class MessageItem {
|
||||
private BlockRef messageRef;
|
||||
private Integer msgSubType;
|
||||
|
||||
+20
@@ -46,7 +46,12 @@ public class Net_ListSubscriptionsFeed_Response extends Net_Response {
|
||||
private String ownerLogin;
|
||||
private String ownerBlockchainName;
|
||||
private String channelName;
|
||||
private String displayName;
|
||||
private String channelDescription;
|
||||
private String avaAr;
|
||||
private String avaSha256;
|
||||
private Long avaSize;
|
||||
private Long metaUpdatedAtMs;
|
||||
private Integer channelTypeCode;
|
||||
private Integer channelTypeVersion;
|
||||
private boolean personal;
|
||||
@@ -61,9 +66,24 @@ public class Net_ListSubscriptionsFeed_Response extends Net_Response {
|
||||
public String getChannelName() { return channelName; }
|
||||
public void setChannelName(String channelName) { this.channelName = channelName; }
|
||||
|
||||
public String getDisplayName() { return displayName; }
|
||||
public void setDisplayName(String displayName) { this.displayName = displayName; }
|
||||
|
||||
public String getChannelDescription() { return channelDescription; }
|
||||
public void setChannelDescription(String channelDescription) { this.channelDescription = channelDescription; }
|
||||
|
||||
public String getAvaAr() { return avaAr; }
|
||||
public void setAvaAr(String avaAr) { this.avaAr = avaAr; }
|
||||
|
||||
public String getAvaSha256() { return avaSha256; }
|
||||
public void setAvaSha256(String avaSha256) { this.avaSha256 = avaSha256; }
|
||||
|
||||
public Long getAvaSize() { return avaSize; }
|
||||
public void setAvaSize(Long avaSize) { this.avaSize = avaSize; }
|
||||
|
||||
public Long getMetaUpdatedAtMs() { return metaUpdatedAtMs; }
|
||||
public void setMetaUpdatedAtMs(Long metaUpdatedAtMs) { this.metaUpdatedAtMs = metaUpdatedAtMs; }
|
||||
|
||||
public Integer getChannelTypeCode() { return channelTypeCode; }
|
||||
public void setChannelTypeCode(Integer channelTypeCode) { this.channelTypeCode = channelTypeCode; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user