编译Telegram代码,群语音版本

  • Telegram github地址
  • 编译环境
  • 准备工作
  • 开始编译
  • 拉取代码
  • 检出7.6.1
  • Android studio打开telegram项目
  • 去除一些代码
  • 运行项目
  • 如果想看tg的数据,打印log日志查看
  • 接收离线推送(谷歌推送)
  • 注意


Telegram github地址

github: https://github.com/DrKLO/Telegram.git

编译环境

Windows 64
Android studio 4.2
ndk版本:21.1.6352462
JDK1.8

准备工作

  1. 去telegram官网下载app,注意需要vpn才能连接到,app也同样需要vpn才可以使用。
  2. 使用手机号登录telegram app
  3. 打开https://my.telegram.org/,输入刚才在app内登录的账号如 +8618312345678,点击NEXT获取验证码,验证码会发到app内,
  4. 登录后打开 API development tools,填写app名称,和shortname,比如 testapp1,选择Android,最后点击 Create application 创建完成

开始编译

拉取代码

git clone https://github.com/DrKLO/Telegram.git

检出7.6.1

我们的项目目前使用到群语音版本 7.6.1,如果是连接的是telegram的服务,不需要做服务端的,如果想自己做服务端的接口,去参考官方的api标准,
我们的app服务端是自己做的,所以群语音也需要自己做,后面会出一个自己做的群语音服务的教程,基于mediasoup,以及官方的Demo讲解。

git checkout 7ba9838a

Android studio打开telegram项目

编辑Telegram\TMessagesProj\src\main\java\org\telegram\messenger\BuildVars.java
替换你在控制台创建的获取的 APP_IDAPP_HASH
下面是对应修改的代码

public class BuildVars {


    public static boolean DEBUG_VERSION = BuildConfig.DEBUG;
    public static boolean DEBUG_PRIVATE_VERSION = DEBUG_VERSION;
    public static boolean LOGS_ENABLED = DEBUG_VERSION;
    public static boolean USE_CLOUD_STRINGS = true;
    public static boolean CHECK_UPDATES = true;
    public static int BUILD_VERSION = BuildConfig.VERSION_CODE;
    public static String BUILD_VERSION_STRING = BuildConfig.VERSION_NAME;
    public static int APP_ID = 1;//你的api_id
    public static String APP_HASH = "你的api_hash";
    //由于要在谷歌商店,必须去掉所有APPCENTER相关的库,否则被拒
    //public static String APPCENTER_HASH = "a5b5c4f5-51da-dedc-9918-d9766a22ca7c";
    //public static String APPCENTER_HASH_DEBUG = "f9726602-67c9-48d2-b5d0-4761f1c1a8f3";
    public static String SMS_HASH = DEBUG_VERSION ? "O2P2z+/jBpJ" : "oLeq9AcOZkT";
    public static String PLAYSTORE_APP_URL = "https://play.google.com/store/apps/details?id=org.telegram.messenger";

    static {
        if (ApplicationLoader.applicationContext != null) {
            SharedPreferences sharedPreferences = ApplicationLoader.applicationContext.getSharedPreferences("systemConfig", Context.MODE_PRIVATE);
            LOGS_ENABLED = sharedPreferences.getBoolean("logsEnabled", DEBUG_VERSION);
        }
    }
}

去除一些代码

  1. AndroidManifest.xml中删除android:manageSpaceActivity=“org.telegram.ui.ExternalActionActivity”,防止想清除数据而没法进入系统清除的界面
  2. 全局搜索 protectionLevel=“signature” 并删除防止手机上装了telegram而权限冲突安装失败
<permission android:name="org.telegram.messenger.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>

运行项目

到此项目应该会成功运行,运行完成即可登录到telegram正式服务器

如果想看tg的数据,打印log日志查看

  1. 添加依赖库
implementation 'org.apache.commons:commons-lang3:3.6'
  1. 创建RecursiveToStringStyle.java,将tg数据转为字符串并格式化
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.telegram.messenger.FileLog;

import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class RecursiveToStringStyle extends ToStringStyle {

    private final int maxDepth;
    private int depth;
    private final String tabs;


    public RecursiveToStringStyle(int maxDepth) {
        this.maxDepth = maxDepth;
        this.depth = 0;
        tabs = StringUtils.repeat("\t", maxDepth);
        setUseShortClassName(true);
        setUseIdentityHashCode(false);
        this.setArrayStart("");
        this.setArrayEnd("");
        this.setArraySeparator(", ");
        this.setNullText("null");
        this.setSummaryObjectStartText("\"<");
        this.setSummaryObjectEndText(">\"");
        setContentStart(" {");
        setFieldSeparator(",\n");
        setFieldSeparatorAtStart(true);
        setFieldNameValueSeparator(": ");
        setContentEnd("}");
    }
    public static String toString(Object value) {
        if (value == null)
            return "null";
        final StringBuffer sb = new StringBuffer(512);
        RecursiveToStringStyle ins = new RecursiveToStringStyle(13);
        ins.appendDetail(sb, null, value);
        return sb.toString();
    }
    private int getDepth() {
        return Math.min(this.maxDepth, this.depth);
    }
    private void padDepth(StringBuffer buffer) {
        buffer.append(tabs, 0, getDepth());
    }
    private StringBuffer appendTabified(StringBuffer buffer, String value) {
        Matcher matcher = Pattern.compile("\n").matcher(value);
        String replacement = "\n" + tabs.substring(0, getDepth());
        while (matcher.find()) {
            matcher.appendReplacement(buffer, replacement);
        }
        matcher.appendTail(buffer);
        return buffer;
    }


    @Override
    protected void appendFieldSeparator(StringBuffer buffer) {
        buffer.append(getFieldSeparator());
        padDepth(buffer);
    }

    @Override
    public void appendStart(StringBuffer buffer, Object object) {
        this.depth++;
        // 开头特殊处理
        boolean is_array = object != null && object.getClass().isArray();
        if(object != null) {
            if (is_array) {
                buffer.append("[");
            } else {
                appendClassName(buffer, object);
                // 类名后添加constructor
                int constructor = 0;
                String classname = object.getClass().getSimpleName();
                if (classname.contains("TL_")) {
                    try {
                        Class clz = object.getClass();
                        constructor = clz.getDeclaredField("constructor").getInt(clz);
                        buffer.append("("+constructor+")");
                        buffer.append("#"+Integer.toHexString(constructor));
                    } catch(Exception e) {
                        FileLog.e(e);
                    }
                }
                appendContentStart(buffer);
            }
            buffer.append("\n");
            padDepth(buffer);
        }
    }

    @Override
    public void appendEnd(StringBuffer buffer, Object object) {
        // 数组结尾特殊处理
        boolean is_array = object != null && object.getClass().isArray();
        boolean is_empty_array = is_array && ((Object[])object).length <= 0;
        super.appendEnd(buffer, object);
        buffer.setLength(buffer.length() - getContentEnd().length());
        if (!is_empty_array) {
            buffer.append("\n");
        }
        this.depth--;
        if (!is_empty_array) {
            padDepth(buffer);
        }
        if (is_array) {
            buffer.append("]");
        } else {
            appendContentEnd(buffer);
        }
    }

    @Override
    protected void removeLastFieldSeparator(StringBuffer buffer) {
        int n = 0;
        for (int i=buffer.length()-1; i>=0; i--) {
            if (Character.isWhitespace(buffer.charAt(i))) {
                n += 1;
            } else {
                break;
            }
        }
        buffer.setLength(buffer.length() - n);
    }

    private boolean noReflectionNeeded(Object value) {
        try {
            return value != null &&
                    (value.getClass().getName().startsWith("java.lang.")
                            || value.getClass().getMethod("toString").getDeclaringClass() != Object.class);
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException(e);
        }
    }

    @Override
    protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
        if (getDepth() >= maxDepth || noReflectionNeeded(value)) {
            appendTabified(buffer, String.valueOf(value));
        } else {
            new ReflectionToStringBuilder(value, this, buffer, null, false, false).toString();
        }
    }

    @Override
    protected void appendDetail(StringBuffer buffer, String fieldName, Collection<?> coll) {
        buffer.append(ReflectionToStringBuilder.toString(coll.toArray(), this, true, true));
    }

    public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
        // 无用字段不打印
        if (fieldName == "disableFree" || fieldName == "networkType") {
            return;
        }
        // 字符串加引号
        if (value instanceof String) {
            String v = "\"" + (String)value + "\"";
            super.append(buffer, fieldName, v, fullDetail);
        } else {
            super.append(buffer, fieldName, value, fullDetail);
        }
    }
    protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
        super.appendInternal(buffer, fieldName, value, detail);
        // 特殊类型需要显示打印出来便于观察
        if (value instanceof Long) {
            buffer.append(" LONG");
        }
    }
}
  1. 在Telegram\TMessagesProj\src\main\java\org\telegram\messenger\MessagesController.java中 修改
  2. 在Telegram\TMessagesProj\src\main\java\org\telegram\tgnet\ConnectionsManager.java中 修改
  3. 由于log过长可能打印不全,修改Telegram\TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java
/*
 * This is the source code of Telegram for Android v. 5.x.x.
 * It is licensed under GNU GPL v. 2 or later.
 * You should have received a copy of the license in this archive (see LICENSE).
 *
 * Copyright Nikolai Kudashov, 2013-2018.
 */

package org.telegram.messenger;

import android.text.TextUtils;
import android.util.Log;

import org.telegram.messenger.time.FastDateFormat;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.Locale;

public class FileLog {
    private OutputStreamWriter streamWriter = null;
    private FastDateFormat dateFormat = null;
    private DispatchQueue logQueue = null;
    private File currentFile = null;
    private File networkFile = null;
    private File tonlibFile = null;
    private boolean initied;

    private final static String tag = "tmessages";

    private static volatile FileLog Instance = null;
    public static FileLog getInstance() {
        FileLog localInstance = Instance;
        if (localInstance == null) {
            synchronized (FileLog.class) {
                localInstance = Instance;
                if (localInstance == null) {
                    Instance = localInstance = new FileLog();
                }
            }
        }
        return localInstance;
    }

    public FileLog() {
        if (!BuildVars.LOGS_ENABLED) {
            return;
        }
        init();
    }

    public void init() {
        if (initied) {
            return;
        }
        dateFormat = FastDateFormat.getInstance("dd_MM_yyyy_HH_mm_ss", Locale.US);
        try {
            File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null);
            if (sdCard == null) {
                return;
            }
            File dir = new File(sdCard.getAbsolutePath() + "/logs");
            dir.mkdirs();
            currentFile = new File(dir, dateFormat.format(System.currentTimeMillis()) + ".txt");
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            logQueue = new DispatchQueue("logQueue");
            currentFile.createNewFile();
            FileOutputStream stream = new FileOutputStream(currentFile);
            streamWriter = new OutputStreamWriter(stream);
            streamWriter.write("-----start log " + dateFormat.format(System.currentTimeMillis()) + "-----\n");
            streamWriter.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        initied = true;
    }

    public static void ensureInitied() {
        getInstance().init();
    }

    public static String getNetworkLogPath() {
        if (!BuildVars.LOGS_ENABLED) {
            return "";
        }
        try {
            File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null);
            if (sdCard == null) {
                return "";
            }
            File dir = new File(sdCard.getAbsolutePath() + "/logs");
            dir.mkdirs();
            getInstance().networkFile = new File(dir, getInstance().dateFormat.format(System.currentTimeMillis()) + "_net.txt");
            return getInstance().networkFile.getAbsolutePath();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return "";
    }

    public static String getTonlibLogPath() {
        if (!BuildVars.LOGS_ENABLED) {
            return "";
        }
        try {
            File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null);
            if (sdCard == null) {
                return "";
            }
            File dir = new File(sdCard.getAbsolutePath() + "/logs");
            dir.mkdirs();
            getInstance().tonlibFile = new File(dir, getInstance().dateFormat.format(System.currentTimeMillis()) + "_tonlib.txt");
            return getInstance().tonlibFile.getAbsolutePath();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return "";
    }

    public static void e(final String message, final Throwable exception) {
        if (!BuildVars.LOGS_ENABLED) {
            return;
        }
        ensureInitied();
        Log.e(tag, message, exception);
        if (getInstance().streamWriter != null) {
            getInstance().logQueue.postRunnable(() -> {
                try {
                    getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " E/tmessages: " + message + "\n");
                    getInstance().streamWriter.write(exception.toString());
                    getInstance().streamWriter.flush();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }

    public static void e(final String message) {

        if (!BuildVars.LOGS_ENABLED) {
            return;
        }
        ensureInitied();
        logger("e",message);
        if (getInstance().streamWriter != null) {
            getInstance().logQueue.postRunnable(() -> {
                try {
                    getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " E/tmessages: " + message + "\n");
                    getInstance().streamWriter.flush();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }


    public static void e(final Throwable e) {
        if (!BuildVars.LOGS_ENABLED) {
            return;
        }
        ensureInitied();
        e.printStackTrace();
        if (getInstance().streamWriter != null) {
            getInstance().logQueue.postRunnable(() -> {
                try {
                    getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " E/tmessages: " + e + "\n");
                    StackTraceElement[] stack = e.getStackTrace();
                    for (int a = 0; a < stack.length; a++) {
                        getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " E/tmessages: " + stack[a] + "\n");
                    }
                    getInstance().streamWriter.flush();
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            });
        } else {
            e.printStackTrace();
        }
    }

    public static void d(final String message) {
        if (!BuildVars.LOGS_ENABLED) {
            return;
        }
        ensureInitied();
        logger("d",message);
        if (getInstance().streamWriter != null) {
            getInstance().logQueue.postRunnable(() -> {
                try {
                    getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " D/tmessages: " + message + "\n");
                    getInstance().streamWriter.flush();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }

    public static void w(final String message) {
        if (!BuildVars.LOGS_ENABLED) {
            return;
        }
        ensureInitied();
        logger("w",message);
        if (getInstance().streamWriter != null) {
            getInstance().logQueue.postRunnable(() -> {
                try {
                    getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " W/tmessages: " + message + "\n");
                    getInstance().streamWriter.flush();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }

    public static void cleanupLogs() {
        ensureInitied();
        File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null);
        if (sdCard == null) {
            return;
        }
        File dir = new File (sdCard.getAbsolutePath() + "/logs");
        File[] files = dir.listFiles();
        if (files != null) {
            for (int a = 0; a < files.length; a++) {
                File file = files[a];
                if (getInstance().currentFile != null && file.getAbsolutePath().equals(getInstance().currentFile.getAbsolutePath())) {
                    continue;
                }
                if (getInstance().networkFile != null && file.getAbsolutePath().equals(getInstance().networkFile.getAbsolutePath())) {
                    continue;
                }
                if (getInstance().tonlibFile != null && file.getAbsolutePath().equals(getInstance().tonlibFile.getAbsolutePath())) {
                    continue;
                }
                file.delete();
            }
        }
    }

    public static String tagPrefix = "FileLog";//log前缀
    /**
     *
     * @param type logger级别
     * @param o   logger内容

     */
    private static void logger(String type, String o) {
        if (!BuildVars.DEBUG_VERSION) {
            return;
        }
        String msg=o+"";
        String tag = getTag(getCallerStackTraceElement());
        truncation(type,tag,msg);
    }
    /**
     * 截断输出日志
     * @param msg
     */
    private static void truncation(String type,String tag, String msg) {
        if (tag == null || tag.length() == 0
                || msg == null || msg.length() == 0)
            return;

        int segmentSize = 3 * 1024;
        long length = msg.length();
        if (length <= segmentSize ) {// 长度小于等于限制直接打印
            logPrint(type,tag, msg);
        }else {
            while (msg.length() > segmentSize ) {// 循环分段打印日志
                String logContent = msg.substring(0, segmentSize );
                msg = msg.replace(logContent, "");
                logPrint(type,tag, logContent);
            }
            logPrint(type,tag, msg);// 打印剩余日志
        }
    }
    private static void logPrint(String type,String tag,String msg){
        switch (type){
            case  "i":
                Log.i(tag,msg);
            case  "d":
                Log.d(tag,msg);
                break;
            case  "e":
                Log.e(tag,msg);
                break;
            case  "w":
                Log.w(tag,msg);
                break;
        }
    }


    private static String getTag(StackTraceElement element) {
        String tag = "%s.%s(Line:%d)"; // 占位符
        String callerClazzName = element.getClassName(); // 获取到类名

        callerClazzName = callerClazzName.substring(callerClazzName
                .lastIndexOf(".") + 1);
        tag = String.format(tag, callerClazzName, element.getMethodName(),
                element.getLineNumber()); // 替换
        tag = TextUtils.isEmpty(tagPrefix) ? tag : tagPrefix + ":"
                + tag;
        return tag;
    }

    /**
     * 获取线程状态
     * @return
     */
    private static StackTraceElement getCallerStackTraceElement() {
        return Thread.currentThread().getStackTrace()[5];
    }
}
  1. 至此可以快乐地抓取tg数据了

接收离线推送(谷歌推送)

  1. 打开 https://console.firebase.google.com/ 注册一个账号
  2. 创建一个项目 telegram,项目创建完成后添加两个安卓应用org.telegram.messenger 和 org.telegram.messenger.beta
  3. build.gradle里替换为你的签名文件,打开项目设置,为两个应用分别添加SHA证书,SHA1和SHA256,如何获取SHA证书,看下面
  4. 打开Android studio右侧的Gradle
  5. 在firebase控制台,项目配置-》云消息传递,复制密钥,然后打开https://my.telegram.org/apps,配置GCM API key,就是刚才复制的key保存就ok了

注意

如果想编译其他版本,低于 7.1.0 的版本windows上是编译不了的,因为目录太长,超过了windows的最大长度限制,解决办法,下载 VMware 创建ubuntu虚拟机,编译过程都是一样的

第一次写博客,嗨,dia,dia,dia,万能秘籍,上上下下,左右左右,BABA~