编译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
准备工作
- 去telegram官网下载app,注意需要vpn才能连接到,app也同样需要vpn才可以使用。
- 使用手机号登录telegram app
- 打开https://my.telegram.org/,输入刚才在app内登录的账号如 +8618312345678,点击NEXT获取验证码,验证码会发到app内,
- 登录后打开 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_ID 和 APP_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);
}
}
}
去除一些代码
- AndroidManifest.xml中删除android:manageSpaceActivity=“org.telegram.ui.ExternalActionActivity”,防止想清除数据而没法进入系统清除的界面
- 全局搜索 protectionLevel=“signature” 并删除防止手机上装了telegram而权限冲突安装失败
<permission android:name="org.telegram.messenger.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
运行项目
到此项目应该会成功运行,运行完成即可登录到telegram正式服务器
如果想看tg的数据,打印log日志查看
- 添加依赖库
implementation 'org.apache.commons:commons-lang3:3.6'
- 创建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");
}
}
}
- 在Telegram\TMessagesProj\src\main\java\org\telegram\messenger\MessagesController.java中 修改
- 在Telegram\TMessagesProj\src\main\java\org\telegram\tgnet\ConnectionsManager.java中 修改
- 由于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];
}
}
- 至此可以快乐地抓取tg数据了
接收离线推送(谷歌推送)
- 打开 https://console.firebase.google.com/ 注册一个账号
- 创建一个项目 telegram,项目创建完成后添加两个安卓应用org.telegram.messenger 和 org.telegram.messenger.beta
- build.gradle里替换为你的签名文件,打开项目设置,为两个应用分别添加SHA证书,SHA1和SHA256,如何获取SHA证书,看下面
- 打开Android studio右侧的Gradle
- 在firebase控制台,项目配置-》云消息传递,复制密钥,然后打开https://my.telegram.org/apps,配置GCM API key,就是刚才复制的key保存就ok了
注意
如果想编译其他版本,低于 7.1.0 的版本windows上是编译不了的,因为目录太长,超过了windows的最大长度限制,解决办法,下载 VMware 创建ubuntu虚拟机,编译过程都是一样的
第一次写博客,嗨,dia,dia,dia,万能秘籍,上上下下,左右左右,BABA~